schild.rb 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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 eine Verbindung zu einem Schild-Server her. Sollte nur aufgerufen werden, wenn wechselnde Verbindungen nötig sind.
  85. def self.connect
  86. Sequel.connect(:adapter=>ENV['S_ADAPTER'], :host=>ENV['S_HOST'], :user=>ENV['S_USER'], :password=>ENV['S_PASSWORD'], :database=>ENV['S_DB'])
  87. end
  88. # Stellt die Schüler-Tabelle samt Assoziationen bereit.
  89. class Schueler < Sequel::Model(:schueler)
  90. many_to_one :fachklasse, :class => :Fachklasse, :key => :Fachklasse_ID
  91. one_to_many :abschnitte, :class => :Abschnitt
  92. one_to_one :bk_abschluss, :class => :BKAbschluss
  93. one_to_many :bk_abschluss_leistungen, :class => :BKAbschlussFaecher
  94. one_to_one :abi_abschluss, :class => :AbiAbschluss
  95. one_to_many :abi_abschluss_leistungen, :class => :AbiAbschlussFaecher
  96. one_to_many :vermerke, :class => :Vermerke
  97. end
  98. # Dient als Assoziation für Schüler und deren Klassenbezeichnung etc.
  99. class Fachklasse < Sequel::Model(:eigeneschule_fachklassen)
  100. one_to_many :schueler
  101. end
  102. # Assoziation für Lehrer, hauptsächlich für Klassenlehrer
  103. class Klassenlehrer < Sequel::Model(:k_lehrer)
  104. one_to_one :abschnitt, :primary_key=>:Kuerzel, :key=>:KlassenLehrer
  105. end
  106. # Ist die Assoziation, die Halbjahre, sog. Abschnitte zurückgibt.
  107. class Abschnitt < Sequel::Model(:schuelerlernabschnittsdaten)
  108. many_to_one :schueler, :class => :Schueler, :key => :Schueler_ID
  109. one_to_many :noten, :class => :Noten
  110. many_to_one :klassenlehrer, :class => :Klassenlehrer, :primary_key=>:Kuerzel, :key=>:KlassenLehrer
  111. end
  112. # Assoziation für Noten
  113. class Noten < Sequel::Model(:schuelerleistungsdaten)
  114. many_to_one :abschnitt, :class => :Abschnitt, :key => :Abschnitt_ID
  115. many_to_one :fach, :class => :Faecher, :key => :Fach_ID
  116. end
  117. # Assoziation für Fächer
  118. class Faecher < Sequel::Model(:eigeneschule_faecher)
  119. #siehe abi_...
  120. one_to_one :noten
  121. one_to_many :abi_abschluss_leistungen
  122. one_to_one :sprachenfolge, :class => :Sprachenfolge, :key => :Fach_ID
  123. end
  124. # Assoziation für BK-Abschluss des Schülers
  125. class BKAbschluss < Sequel::Model(:schuelerbkabschluss)
  126. one_to_one :schueler
  127. end
  128. # Assoziation für die Prüfungsfächer des Schülers
  129. class BKAbschlussFaecher < Sequel::Model(:schuelerbkfaecher)
  130. many_to_one :schueler
  131. end
  132. # Assoziation für Abi-Abschluss des Schülers
  133. class AbiAbschluss < Sequel::Model(:schuelerabitur)
  134. one_to_one :schueler
  135. end
  136. # Assoziation für die Abifächer des Schülers
  137. class AbiAbschlussFaecher < Sequel::Model(:schuelerabifaecher)
  138. many_to_one :schueler
  139. many_to_one :fach, :class => :Faecher, :key => :Fach_ID
  140. end
  141. # Assoziation für die bisher erreichten Sprachniveaus
  142. class Sprachenfolge < Sequel::Model(:schuelersprachenfolge)
  143. one_to_one :Faecher
  144. end
  145. # Vermerke von Schülern
  146. class Vermerke < Sequel::Model(:schuelervermerke)
  147. many_to_one :Schueler
  148. end
  149. # Schul-Tabelle
  150. class Schule < Sequel::Model(:eigeneschule)
  151. end
  152. # Tabelle für Schild-Nutzer
  153. class Nutzer < Sequel::Model(:users)
  154. end
  155. end
  156. module SchildErweitert
  157. include Schild
  158. # Stellt die Schüler-Tabelle samt Assoziationen bereit.
  159. class Schueler < Schild::Schueler
  160. include SchildTypeSaver
  161. # gibt das z.Zt. aktuelle Halbjahr zurück.
  162. def akt_halbjahr
  163. abschnitte.last
  164. end
  165. # gibt aus +jahr+ das Halbjahr +1+ oder +2+ zurück.
  166. def halbjahr(jahr, abschnitt)
  167. abschnitte_dataset.where(:jahr => jahr, :abschnitt => abschnitt).first
  168. end
  169. # gibt +Herr+ oder +Frau+ als Anrede für Schüler zurück.
  170. def anrede
  171. self.geschlecht == 3 ? "Herr" : "Frau"
  172. end
  173. # gibt die passende Bezeichnung zurück Schüler
  174. def schueler_in
  175. self.geschlecht == 3 ? "Schüler" : "Schülerin"
  176. end
  177. # gibt die passende Bezeichnung zurück Studierende
  178. def studierende_r
  179. self.geschlecht == 3 ? "Studierender" : "Studierende"
  180. end
  181. # gibt die jeweilige Berufsbezeichnung nach Geschlecht zurück.
  182. def berufsbezeichnung_mw
  183. self.geschlecht == 3 ? self.fachklasse.bezeichnung : self.fachklasse.beschreibung_w
  184. end
  185. # gibt +true+ zurück, wenn Schüler volljährig.
  186. def volljaehrig?
  187. self.volljaehrig == "+"
  188. end
  189. def foto_vorhanden?
  190. self.foto_vorhanden == "+"
  191. end
  192. end
  193. # Dient als Assoziation für Schüler und deren Klassenbezeichnung etc.
  194. class Fachklasse < Schild::Fachklasse
  195. include SchildTypeSaver
  196. end
  197. # Assoziation für Lehrer, hauptsächlich für Klassenlehrer
  198. class Klassenlehrer < Schild::Klassenlehrer
  199. include SchildTypeSaver
  200. end
  201. # Ist die Assoziation, die Halbjahre, sog. Abschnitte zurückgibt.
  202. class Abschnitt < Schild::Abschnitt
  203. include SchildTypeSaver
  204. dataset_module do
  205. # filtert den Datensatz nach Jahr
  206. def jahr(i)
  207. where(:Jahr => i)
  208. end
  209. # filtert den Datensatz nach Halbjahr
  210. def halbjahr(i,j)
  211. jahr(i).where(:Abschnitt => j)
  212. end
  213. # filtert und gibt den Datensatz als Abschnitt des aktuellen Halbjahrs zurück
  214. def akt_halbjahr
  215. halbjahr(Time.new.year-1, 1).first
  216. end
  217. end
  218. # Hilfsmethode für die folgenden Methoden
  219. def faecher_nach_id(id)
  220. noten.select{ |n| n.fach.Fachgruppe_ID == id && n.AufZeugnis == '+' }.sort_by{ |n| n.fach.SortierungS2 }
  221. end
  222. # wählt alle berufsübergreifenden Fächer des gewählten Schülers in angegeben Halbjahr.
  223. def berufsuebergreifend
  224. faecher_nach_id 10
  225. end
  226. # wählt alle berufsbezogenen Fächer des gewählten Schülers in angegeben Halbjahr.
  227. def berufsbezogen
  228. faecher_nach_id 20
  229. end
  230. # wählt alle Fächer des Differenzierungsbreichs des gewählten Schülers in angegeben Halbjahr.
  231. def differenzierungsbereich
  232. faecher_nach_id 30
  233. end
  234. # wählt alle Fächergruppen aus.
  235. def faechergruppen
  236. [berufsuebergreifend, berufsbezogen, differenzierungsbereich]
  237. end
  238. # gibt den Namen des Klassenlehrers mit gekürztem Vornamen.
  239. def v_name_klassenlehrer
  240. v = klassenlehrer.vorname
  241. n = klassenlehrer.nachname
  242. "#{v[0]}. #{n}"
  243. end
  244. # gibt "Klassenlehrer" entsprechend Geschlecht zurück
  245. def klassenlehrer_in
  246. klassenlehrer.geschlecht == "3" ? "Klassenlehrer" : "Klassenlehrerin"
  247. end
  248. # gibt das aktuelle Schuljahr als String im Format "2014/15" zurück.
  249. def schuljahr
  250. jahr = self.jahr
  251. "#{jahr}/#{jahr-1999}"
  252. end
  253. end
  254. # Assoziation für Noten
  255. class Noten < Schild::Noten
  256. include SchildTypeSaver
  257. include NotenHelfer
  258. # note in String umwandeln
  259. def note
  260. note_s self.noten_krz
  261. end
  262. # Bezeichnung des Fachs
  263. def bezeichnung
  264. fach.bezeichnung
  265. end
  266. # Die Fachgruppen ID des Fachs
  267. def fachgruppe_ID
  268. fach.fachgruppe_id
  269. end
  270. end
  271. # Assoziation für Fächer
  272. class Faecher < Schild::Faecher
  273. include SchildTypeSaver
  274. end
  275. # Assoziation für BK-Abschlussdaten
  276. class BKAbschluss < Schild::BKAbschluss
  277. include SchildTypeSaver
  278. # Ist der Schüler zugelassen?
  279. def zulassung?
  280. self.Zulassung == "+"
  281. end
  282. # Ist der Schüler für den Berufsabschluss zugelassen?
  283. def zulassung_ba?
  284. self.ZulassungBA == "+"
  285. end
  286. # Hat der Schüler den Berufsabschluss bestanden?
  287. def bestanden_ba?
  288. self.BestandenBA == "+"
  289. end
  290. end
  291. # Assoziation für die jeweiligen BK-Prüfungsfächer
  292. class BKAbschlussFaecher < Schild::BKAbschlussFaecher
  293. include SchildTypeSaver
  294. include NotenHelfer
  295. # Wurde das Fach schriftlich geprüft?
  296. def fach_schriftlich?
  297. self.FachSchriftlich == "+"
  298. end
  299. # Wurde das Fach mündlich geprüft?
  300. def fach_muendlich?
  301. self.MdlPruefung == "+"
  302. end
  303. def note(notenart=:note_abschluss_ba)
  304. note_s send(notenart)
  305. end
  306. end
  307. # Assoziation für Abi-Abschlussdaten
  308. class AbiAbschluss < Schild::AbiAbschluss
  309. include SchildTypeSaver
  310. end
  311. # Assoziation für die jeweiligen Abi-Prüfungsfächer
  312. class AbiAbschlussFaecher < Schild::AbiAbschlussFaecher
  313. include SchildTypeSaver
  314. include NotenHelfer
  315. def note(notenart)
  316. note_s send(notenart)
  317. end
  318. end
  319. class Sprachenfolge < Schild::Sprachenfolge
  320. include SchildTypeSaver
  321. end
  322. class Vermerke < Schild::Vermerke
  323. include SchildTypeSaver
  324. end
  325. # Schul-Tabelle mit vereinfachtem Zugriff auf Datenfelder.
  326. class Schule < Schild::Schule
  327. include SchildTypeSaver
  328. # gibt die Schulnummer zurück
  329. def self.schulnummer
  330. self.first.schul_nr
  331. end
  332. def self.v_name_schulleiter
  333. "#{self.first.schulleiter_vorname[0]}. #{self.first.schulleiter_name}"
  334. end
  335. def self.schulleiter_in
  336. self.first.schulleiter_geschlecht == 3 ? "Schulleiter" : "Schulleiterin"
  337. end
  338. def self.ort
  339. self.first.ort
  340. end
  341. end
  342. # Tabelle der Schuld-Benutzer zum Abgleichen der Daten
  343. class Nutzer < Schild::Nutzer
  344. include SchildTypeSaver
  345. def name
  346. self.us_name
  347. end
  348. def login
  349. self.us_login_name
  350. end
  351. def passwort
  352. self.us_password
  353. end
  354. alias :password :passwort
  355. def passwort?(passwort='')
  356. crypt(passwort) == self.passwort
  357. end
  358. alias :password? :passwort?
  359. def crypt(passwort)
  360. passwort.codepoints.map{|c| ((c/16)*32+15-c).chr}.join('')
  361. end
  362. end
  363. end