schild.rb 12 KB

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