schild.rb 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. require 'schild/version'
  2. require 'sequel'
  3. # erst Ruby 2.1.0 macht include zu einer public-Methode
  4. if Module.private_method_defined? :include
  5. class Module
  6. public :include
  7. end
  8. end
  9. # String und Symbol werden um snake_case ergänzt, das die Schild-Tabellen umbenennt
  10. module CoreExtensions
  11. module String
  12. def snake_case
  13. return downcase if match(/\A[A-Z]+\z/)
  14. gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
  15. gsub(/([a-z])([A-Z])/, '\1_\2').
  16. downcase
  17. end
  18. end
  19. module Symbol
  20. def snake_case
  21. to_s.snake_case
  22. end
  23. end
  24. end
  25. # Schild hat teilweise nil in DB-Feldern. SchildTypeSaver gibt entweder einen
  26. # "Fehlt"-String zurück oder bei strftime das 1899 Datum zurück.
  27. module SchildTypeSaver
  28. Symbol.include CoreExtensions::Symbol
  29. String.include CoreExtensions::String
  30. # bei include wird für jede Spalte in der Schild-Tabelle eine Ersatzmethode
  31. # erstellt, die bei nil ein Null-Objekt erstellt.
  32. def self.included(klass)
  33. klass.columns.each do |column|
  34. name = column.snake_case
  35. define_method(name) { public_send(column) || create_null_object(klass, column)}
  36. end
  37. end
  38. def create_null_object(klass, column)
  39. k = DB.schema_type_class(klass.db_schema[column][:type])
  40. if k.class == Array
  41. # Sequel stellt :datetime als [Time, DateTime] dar, deswegen die Abfrage nach Array
  42. # Schild verwendet Time Objekte, wir machen das auch
  43. Time.new(1899)
  44. elsif k == Integer
  45. 0
  46. else
  47. # alle anderen types werden als Klasse zurückgegeben
  48. k.new
  49. end
  50. end
  51. end
  52. # Mixin für Notenbezeichnungen
  53. module NotenHelfer
  54. # Notenbezeichnung als String
  55. def note_s(ziffer)
  56. case ziffer
  57. when "1", "1+", "1-"
  58. "sehr gut"
  59. when "2", "2+", "2-"
  60. "gut"
  61. when "3", "3+", "3-"
  62. "befriedigend"
  63. when "4", "4+", "4-"
  64. "ausreichend"
  65. when "5", "5+", "5-"
  66. "mangelhaft"
  67. when "6"
  68. "ungenügend"
  69. when 'NB'
  70. "––––––"
  71. when "E1"
  72. "mit besonderem Erfolg teilgenommen"
  73. when "E2"
  74. "mit Erfolg teilgenommen"
  75. when 'E3'
  76. "teilgenommen"
  77. end
  78. end
  79. end
  80. # Das Schild Modul, das alle Klassen für die Datenbankanbindung bereitstellt
  81. module Schild
  82. # ist die Datenbank-Verbindung. Alle Daten können über diese Konstante abgerufen werden
  83. DB = Sequel.connect(:adapter=>ENV['S_ADAPTER'], :host=>ENV['S_HOST'], :user=>ENV['S_USER'], :password=>ENV['S_PASSWORD'], :database=>ENV['S_DB'])
  84. # Stellt die Schüler-Tabelle samt Assoziationen bereit.
  85. class Schueler < Sequel::Model(:schueler)
  86. many_to_one :fachklasse, :class => :Fachklasse, :key => :Fachklasse_ID
  87. one_to_many :abschnitte, :class => :Abschnitt
  88. one_to_one :bk_abschluss, :class => :BKAbschluss
  89. one_to_many :bk_abschluss_leistungen, :class => :BKAbschlussFaecher
  90. one_to_one :abi_abschluss, :class => :AbiAbschluss
  91. one_to_many :abi_abschluss_leistungen, :class => :AbiAbschlussFaecher
  92. one_to_many :vermerke, :class => :Vermerke
  93. end
  94. # Dient als Assoziation für Schüler und deren Klassenbezeichnung etc.
  95. class Fachklasse < Sequel::Model(:eigeneschule_fachklassen)
  96. one_to_many :schueler
  97. end
  98. # Assoziation für Lehrer, hauptsächlich für Klassenlehrer
  99. class Klassenlehrer < Sequel::Model(:k_lehrer)
  100. one_to_one :abschnitt, :primary_key=>:Kuerzel, :key=>:KlassenLehrer
  101. end
  102. # Ist die Assoziation, die Halbjahre, sog. Abschnitte zurückgibt.
  103. class Abschnitt < Sequel::Model(:schuelerlernabschnittsdaten)
  104. many_to_one :schueler, :class => :Schueler, :key => :Schueler_ID
  105. one_to_many :noten, :class => :Noten
  106. many_to_one :klassenlehrer, :class => :Klassenlehrer, :primary_key=>:Kuerzel, :key=>:KlassenLehrer
  107. end
  108. # Assoziation für Noten
  109. class Noten < Sequel::Model(:schuelerleistungsdaten)
  110. many_to_one :abschnitt, :class => :Abschnitt, :key => :Abschnitt_ID
  111. many_to_one :fach, :class => :Faecher, :key => :Fach_ID
  112. end
  113. # Assoziation für Fächer
  114. class Faecher < Sequel::Model(:eigeneschule_faecher)
  115. #siehe abi_...
  116. one_to_one :noten
  117. one_to_many :abi_abschluss_leistungen
  118. one_to_one :sprachenfolge, :class => :Sprachenfolge, :key => :Fach_ID
  119. end
  120. # Assoziation für BK-Abschluss des Schülers
  121. class BKAbschluss < Sequel::Model(:schuelerbkabschluss)
  122. one_to_one :schueler
  123. end
  124. # Assoziation für die Prüfungsfächer des Schülers
  125. class BKAbschlussFaecher < Sequel::Model(:schuelerbkfaecher)
  126. many_to_one :schueler
  127. end
  128. # Assoziation für Abi-Abschluss des Schülers
  129. class AbiAbschluss < Sequel::Model(:schuelerabitur)
  130. one_to_one :schueler
  131. end
  132. # Assoziation für die Abifächer des Schülers
  133. class AbiAbschlussFaecher < Sequel::Model(:schuelerabifaecher)
  134. many_to_one :schueler
  135. many_to_one :fach, :class => :Faecher, :key => :Fach_ID
  136. end
  137. # Assoziation für die bisher erreichten Sprachniveaus
  138. class Sprachenfolge < Sequel::Model(:schuelersprachenfolge)
  139. one_to_one :Faecher
  140. end
  141. # Vermerke von Schülern
  142. class Vermerke < Sequel::Model(:schuelervermerke)
  143. many_to_one :Schueler
  144. end
  145. # Schul-Tabelle
  146. class Schule < Sequel::Model(:eigeneschule)
  147. end
  148. # Tabelle für Schild-Nutzer
  149. class Nutzer < Sequel::Model(:users)
  150. end
  151. end
  152. module SchildErweitert
  153. include Schild
  154. # Stellt die Schüler-Tabelle samt Assoziationen bereit.
  155. class Schueler < Schild::Schueler
  156. include SchildTypeSaver
  157. # gibt das z.Zt. aktuelle Halbjahr zurück.
  158. def akt_halbjahr
  159. abschnitte.last
  160. end
  161. # gibt aus +jahr+ das Halbjahr +1+ oder +2+ zurück.
  162. def halbjahr(jahr, abschnitt)
  163. abschnitte_dataset.where(:jahr => jahr, :abschnitt => abschnitt).first
  164. end
  165. # gibt +Herr+ oder +Frau+ als Anrede für Schüler zurück.
  166. def anrede
  167. self.geschlecht == 3 ? "Herr" : "Frau"
  168. end
  169. # gibt die passende Bezeichnung zurück Schüler
  170. def schueler_in
  171. self.geschlecht == 3 ? "Schüler" : "Schülerin"
  172. end
  173. # gibt die passende Bezeichnung zurück Studierende
  174. def studierende_r
  175. self.geschlecht == 3 ? "Studierender" : "Studierende"
  176. end
  177. # gibt die jeweilige Berufsbezeichnung nach Geschlecht zurück.
  178. def berufsbezeichnung_mw
  179. self.geschlecht == 3 ? self.fachklasse.bezeichnung : self.fachklasse.beschreibung_w
  180. end
  181. # gibt +true+ zurück, wenn Schüler volljährig.
  182. def volljaehrig?
  183. self.volljaehrig == "+"
  184. end
  185. # gibt an, ob der Schüler zu einem Zeitpunkt *datum* volljährig war.
  186. def volljaehrig_bei?(datum)
  187. geb, datum = self.geburtsdatum.to_date, datum.to_date
  188. (datum.year - geb.year - ((datum.month > geb.month || (datum.month == geb.month && datum.day >= geb.day)) ? 0 : 1)) >= 18
  189. end
  190. def foto_vorhanden?
  191. self.foto_vorhanden == "+"
  192. end
  193. end
  194. # Dient als Assoziation für Schüler und deren Klassenbezeichnung etc.
  195. class Fachklasse < Schild::Fachklasse
  196. include SchildTypeSaver
  197. end
  198. # Assoziation für Lehrer, hauptsächlich für Klassenlehrer
  199. class Klassenlehrer < Schild::Klassenlehrer
  200. include SchildTypeSaver
  201. end
  202. # Ist die Assoziation, die Halbjahre, sog. Abschnitte zurückgibt.
  203. class Abschnitt < Schild::Abschnitt
  204. include SchildTypeSaver
  205. dataset_module do
  206. # filtert den Datensatz nach Jahr
  207. def jahr(i)
  208. where(:Jahr => i)
  209. end
  210. # filtert den Datensatz nach Halbjahr
  211. def halbjahr(i,j)
  212. jahr(i).where(:Abschnitt => j)
  213. end
  214. # filtert und gibt den Datensatz als Abschnitt des aktuellen Halbjahrs zurück
  215. def akt_halbjahr
  216. halbjahr(Time.new.year-1, 1).first
  217. end
  218. end
  219. # Hilfsmethode für die folgenden Methoden
  220. def faecher_nach_id(id)
  221. noten.select{ |n| n.fach.Fachgruppe_ID == id && n.AufZeugnis == '+' }.sort_by{ |n| n.fach.SortierungS2 }
  222. end
  223. # wählt alle berufsübergreifenden Fächer des gewählten Schülers in angegeben Halbjahr.
  224. def berufsuebergreifend
  225. faecher_nach_id 10
  226. end
  227. # wählt alle berufsbezogenen Fächer des gewählten Schülers in angegeben Halbjahr.
  228. def berufsbezogen
  229. faecher_nach_id 20
  230. end
  231. # wählt alle Fächer des Differenzierungsbreichs des gewählten Schülers in angegeben Halbjahr.
  232. def differenzierungsbereich
  233. faecher_nach_id 30
  234. end
  235. # wählt alle Fächergruppen aus.
  236. def faechergruppen
  237. [berufsuebergreifend, berufsbezogen, differenzierungsbereich]
  238. end
  239. # gibt den Namen des Klassenlehrers mit gekürztem Vornamen.
  240. def v_name_klassenlehrer
  241. v = klassenlehrer.vorname
  242. n = klassenlehrer.nachname
  243. "#{v[0]}. #{n}"
  244. end
  245. # gibt "Klassenlehrer" entsprechend Geschlecht zurück
  246. def klassenlehrer_in
  247. klassenlehrer.geschlecht == "3" ? "Klassenlehrer" : "Klassenlehrerin"
  248. end
  249. # gibt das aktuelle Schuljahr als String im Format "2014/15" zurück.
  250. def schuljahr
  251. jahr = self.jahr
  252. "#{jahr}/#{jahr-1999}"
  253. end
  254. end
  255. # Assoziation für Noten
  256. class Noten < Schild::Noten
  257. include SchildTypeSaver
  258. include NotenHelfer
  259. # note in String umwandeln
  260. def note
  261. note_s self.noten_krz
  262. end
  263. # Bezeichnung des Fachs
  264. def bezeichnung
  265. fach.bezeichnung
  266. end
  267. # Die Fachgruppen ID des Fachs
  268. def fachgruppe_ID
  269. fach.fachgruppe_id
  270. end
  271. end
  272. # Assoziation für Fächer
  273. class Faecher < Schild::Faecher
  274. include SchildTypeSaver
  275. end
  276. # Assoziation für BK-Abschlussdaten
  277. class BKAbschluss < Schild::BKAbschluss
  278. include SchildTypeSaver
  279. # Ist der Schüler zugelassen?
  280. def zulassung?
  281. self.Zulassung == "+"
  282. end
  283. # Ist der Schüler für den Berufsabschluss zugelassen?
  284. def zulassung_ba?
  285. self.ZulassungBA == "+"
  286. end
  287. # Hat der Schüler den Berufsabschluss bestanden?
  288. def bestanden_ba?
  289. self.BestandenBA == "+"
  290. end
  291. end
  292. # Assoziation für die jeweiligen BK-Prüfungsfächer
  293. class BKAbschlussFaecher < Schild::BKAbschlussFaecher
  294. include SchildTypeSaver
  295. include NotenHelfer
  296. # Wurde das Fach schriftlich geprüft?
  297. def fach_schriftlich?
  298. self.FachSchriftlich == "+"
  299. end
  300. # Wurde das Fach mündlich geprüft?
  301. def fach_muendlich?
  302. self.MdlPruefung == "+"
  303. end
  304. def note(notenart=:note_abschluss_ba)
  305. note_s send(notenart)
  306. end
  307. end
  308. # Assoziation für Abi-Abschlussdaten
  309. class AbiAbschluss < Schild::AbiAbschluss
  310. include SchildTypeSaver
  311. end
  312. # Assoziation für die jeweiligen Abi-Prüfungsfächer
  313. class AbiAbschlussFaecher < Schild::AbiAbschlussFaecher
  314. include SchildTypeSaver
  315. include NotenHelfer
  316. def note(notenart)
  317. note_s send(notenart)
  318. end
  319. end
  320. class Sprachenfolge < Schild::Sprachenfolge
  321. include SchildTypeSaver
  322. end
  323. class Vermerke < Schild::Vermerke
  324. include SchildTypeSaver
  325. end
  326. # Schul-Tabelle mit vereinfachtem Zugriff auf Datenfelder.
  327. class Schule < Schild::Schule
  328. include SchildTypeSaver
  329. # gibt die Schulnummer zurück
  330. def self.schulnummer
  331. self.first.schul_nr
  332. end
  333. def self.v_name_schulleiter
  334. "#{self.first.schulleiter_vorname[0]}. #{self.first.schulleiter_name}"
  335. end
  336. def self.schulleiter_in
  337. self.first.schulleiter_geschlecht == 3 ? "Schulleiter" : "Schulleiterin"
  338. end
  339. def self.ort
  340. self.first.ort
  341. end
  342. end
  343. # Tabelle der Schuld-Benutzer zum Abgleichen der Daten
  344. class Nutzer < Schild::Nutzer
  345. include SchildTypeSaver
  346. def name
  347. self.us_name
  348. end
  349. def login
  350. self.us_login_name
  351. end
  352. def passwort
  353. self.us_password
  354. end
  355. alias :password :passwort
  356. def passwort?(passwort='')
  357. crypt(passwort) == self.passwort
  358. end
  359. alias :password? :passwort?
  360. def crypt(passwort)
  361. passwort.codepoints.map{|c| ((c/16)*32+15-c).chr}.join('')
  362. end
  363. end
  364. end