Top Level Namespace

Includes:
Neo4jBolt

Defined Under Namespace

Modules: Nextcloud Classes: AudioBotRepl, BackgroundRenderer, BitmapFont, DashboardSchema, DumpDatabase, ImageBotRepl, ImportNewsAttic, Integer, InvitationRepl, Main, Neo4jGlobal, Neo4jHelper, NodeBlock, Parser, PixelFont, RandomTag, SchemaRenderer, Script, SetupDatabase, String, Timetable, TimetableRepl

Constant Summary collapse

DASHBOARD_SERVICE =
ENV['DASHBOARD_SERVICE']
BIB_JWT_TTL =
60
BIB_JWT_TTL_EXTRA =
20
TRESOR_SECOND_FACTOR_TTL =
DEVELOPMENT ? 60 * 15 : 60 * 15
TRESOR_JWT_TTL =
60
TRESOR_JWT_TTL_EXTRA =
20
USER_AGENT_PARSER =
UserAgentParser::Parser.new
WEEKDAYS =
['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
HOMEWORK_FEEDBACK_STATES =
['good', 'hmmm', 'lost']
HOMEWORK_FEEDBACK_EMOJIS =
{'good' => '🙂',
'hmmm' => '🤔',
'lost' => '😕'}
HOURS_FOR_KLASSE =
{}
TUNNEL =
16
DEVELOPMENT =

Diese Datei bitte unter credentials.rb speichern und Werte anpassen (bitte keine Credentials in Git committen)


ENV['DEVELOPMENT']
SCHUL_NAME =

Name der Schule

"Beispielschule"
SCHUL_NAME_AN_DATIV =

'an der' oder 'am'

"an der"
SCHUL_ICON =

Schul-Icon

"brand.png"
SCHULLEITUNG_EMAIL =

E-Mail-Adresse der Schulleitung für das Impressum

'schulleitung@beispielschule.de'
SCHUL_MAIL_DOMAIN =

Mail-Domain für SuS-Adressen

"mail.beispielschule.de"
SCHUL_MAIL_LOGIN_URL =

Webmail-Login-Seite, SMTP- und IMAP-Host (nur wichtig für E-Mail-Briefe)

"https://mail.beispielmailhoster.de"
SCHUL_MAIL_LOGIN_SMTP_HOST =
"smtp.beispielmailhoster.de"
SCHUL_MAIL_LOGIN_IMAP_HOST =
"imap.beispielmailhoster.de"
USE_MOCK_NAMES =

Bei Bedarf können alle Namen durch falsche Namen ersetzt werden (für Demozwecke)

false
EXCLUDE_FROM_MOCKIFICATION =

pseudonymisiert werden sollen

[]
SMTP_SERVER =

SMTP Hostname

'smtp.example.com'
IMAP_SERVER =

IMAP Hostname

'imap.example.com'
SMTP_USER =
'dashboard@beispielschule.de'
SMTP_PASSWORD =
'1234_nein_wirklich'
SMTP_DOMAIN =
'beispielschule.de'
SMTP_FROM =
'Dashboard Beispielschule <dashboard@beispielschule.de>'
DASHBOARD_SUPPORT_EMAIL =
'dashboard@beispielschule.de'
DATENTRESOR_HOST =
''
DATENTRESOR_UNLOCKED_FOR =
nil
DATENTRESOR_JWT_APPKEY =
''
SMS_GATEWAY_SECRET =
'bitte_ein_zufälliges_secret_generieren'
DATENTRESOR_HOTLINE =
''
DATENTRESOR_HOTLINE_MIT_NUMMERN =
''
DATENTRESOR_HOTLINE_USERS =
[]
ZEUGNIS_USE_MOCK_NAMES =
false
ZEUGNIS_ADMIN_USERS =
[]
MAILING_LIST_EMAIL =

Mailing-Liste

DEVELOPMENT ? 'verteiler.dev@mail.beispielschule.de' : 'verteiler@mail.beispielschule.de'
MAILING_LIST_PASSWORD =
'1234_bitte_generiere_ein_zufälliges_passwort'
VERTEILER_MAIL_HEADER =
'X-Forwarded-From-Gymnasium-Steglitz'
VERTEILER_TEST_EMAIL =
"verteiler.test@#{SCHUL_MAIL_DOMAIN}"
VERTEILER_DEVELOPMENT_EMAILS =
['admin@beispielschule.de']
MAIL_SUPPORT_NAME =
'Mail-Support Beispielschule'
MAIL_SUPPORT_EMAIL =
'mailsupport@beispielschule.de'
WEBSITE_HOST =

Domain, auf der die Live-Seite läuft

'dashboard.beispielschule.de'
WEBSITE_MAINTAINER_NAME =

Name für Unterschriften in E-Mails (Mit freundlichen Grüßen…)

'Herr Müller'
WEBSITE_MAINTAINER_NAME_AKKUSATIV =
'Herrn Müller'
WEBSITE_MAINTAINER_EMAIL =
'mueller@beispielschule.de'
VOTING_WEBSITE_URL =

Website mit Voting-System (muss noch veröffentlich werden)

'https://abstimmung.beispielschule.de'
VOTING_CONTACT_EMAIL =

Ansprechpartner für Wahlverfahren

'admin@beispielschule.de'
TECHNIK_HILFE_WEBSITE_URL =

Website für Technikhilfe (Chat und Speedtest)

'https://hilfe.beispielschule.de'
WEB_ROOT =
DEVELOPMENT ? 'http://localhost:8025' : "https://#{WEBSITE_HOST}"
ROOM_ORDER =
%w(101 102 103 104)
NEXTCLOUD_URL =

Das Dashboard benötigt einen Nextcloud-Account, der Admin-Rechte hat

'http://localhost:8024'
NEXTCLOUD_URL_FROM_RUBY_CONTAINER =

keine Rolle und der Wert sollte derselbe sein wie für NEXTCLOUD_URL

'http://nextcloud'
NEXTCLOUD_USER =
'dashboard'
NEXTCLOUD_PASSWORD =
'hunter2_bitte_etwas_anderes_waehlen'
NEXTCLOUD_DASHBOARD_DATA_DIRECTORY =

NEXTCLOUD_DASHBOARD_DATA_DIRECTORY muss ein absoluter Pfad sein

__NEXTCLOUD_DASHBOARD_DATA_DIRECTORY__
NEXTCLOUD_WAIT_SECONDS =
__NEXTCLOUD_WAIT_SECONDS__
NEXTCLOUD_ALL_ACCESS_PASSWORD_BE_CAREFUL =

Das Skript share-nc-folders.rb muss sich als jeder Nutzer in der NextCloud anmelden können. Dazu kann die NC-App »External user authentication« verwendet werden, die fehlgeschlagene Anmeldeversuche an eine URL weiterleiten kann, die dann die Authentifizierung übernimmt. Wenn der folgende Wert != nil ist, ist dies das Passwort, mit dem sich das Skript als jeder Nutzer anmelden kann. Vorsicht ist angesagt.

'user_backends' => array(
    array(
        'class' => 'OC_User_BasicAuth',
        'arguments' => array('https://dashboard.beispielschule.de/nc_auth'),
    ),
),
'here_be_dragons_dont_use_this_password'
MATRIX_DOMAIN =
'matrix.example.com'
MATRIX_DOMAIN_SHORT =
'example.com'
MATRIX_ALL_ACCESS_PASSWORD_BE_CAREFUL =
nil
JITSI_HOST =

Das Dashboard vermittelt Links in Jitsi-Räume mit Hilfe von JWT (JSON Web Tokens). Dafür werden ein paar Angaben benötigt, die auf der Jitsi-Seite verifiziert werden müssen.

'meet.beispielschule.de'
JWT_APPAUD =
'jitsi'
JWT_APPISS =
'dashboard'
JWT_APPKEY =
'ein_langer_langer_richtig_langer_app_key'
JWT_SUB =
'beispielschule.de'
JWT_APPKEY_AGRAPP =
'ein_langer_langer_richtig_langer_app_key'
BIB_HOST =
'https://bibliothek.beispielschule.de'
JWT_APPKEY_BIB =
'ein_langer_langer_richtig_langer_app_key'
EMAIL_PASSWORD_SALT =

Es folgen ein paar Salts, die bestimmen, nach welchen Regeln Passwörter und sekundäre IDs generiert werden

'bitte_jeden_salt_nur_einmal_verwenden'
NEXTCLOUD_PASSWORD_SALT =
'bitte_jeden_salt_nur_einmal_verwenden'
KLASSEN_ID_SALT =
'bitte_jeden_salt_nur_einmal_verwenden'
USER_ID_SALT =
'bitte_jeden_salt_nur_einmal_verwenden'
LESSON_ID_SALT =
'bitte_jeden_salt_nur_einmal_verwenden'
SESSION_SCRAMBLER =
'bitte_jeden_salt_nur_einmal_verwenden'
EXTERNAL_USER_EVENT_SCRAMBLER =
'bitte_jeden_salt_nur_einmal_verwenden'
LOGIN_CODE_SALT =
'bitte_jeden_salt_nur_einmal_verwenden'
WEBSITE_READ_INFO_SECRET =
'bitte_ein_zufälliges_secret_generieren'
MESSAGE_DELAY =
DEVELOPMENT ? 1 : 1
LOGIN_STATS_D =
[0, 7, 28, 1000]
VPLAN_ENCODING =
'ISO-8859-1'
JITSI_EVENT_PRE_ENTRY_TOLERANCE =

minutes

DEVELOPMENT ? 2880 : 15
JITSI_EVENT_POST_ENTRY_TOLERANCE =

minutes

DEVELOPMENT ? 2880 : 120
JITSI_LESSON_PRE_ENTRY_TOLERANCE =

minutes

DEVELOPMENT ? 5: 5
JITSI_LESSON_POST_ENTRY_TOLERANCE =

minutes

DEVELOPMENT ? 10: 10
PROVIDE_CLASS_STREAM =
false
3600 * 24 * 365
AVAILABLE_FONTS =
['Roboto', 'Alegreya']
GEN_IMAGE_WIDTHS =
[2048, 1200, 1024, 768, 512, 384, 256].sort
MAINTENANCE_MODE =
false
KLASSEN_TR =
{'8o' => ''}
TIMETABLE_ENTRIES_VISIBLE_AHEAD_DAYS =
7
AUFSICHT_ZEIT =
{1 => '08:00', 2 => '09:00', 3 => '09:55', 4 => '10:40',
6 => '12:50', 7 => '13:40', 8 => '14:30'}
PAUSENAUFSICHT_DAUER =
{1 => 25, 2 => 15, 3 => 15, 4 => 20, 6 => 40, 
7 => 40, 8 => 15}
KLASSEN_ORDER =
['5a', '11', '12']
TABLET_COLORS =
{}
ADMIN_USERS =

Liste aller E-Mail-Adressen von Nutzer*innen, die Administratorenrechte haben sollen

['clarke@beispielschule.de']
CAN_SEE_ALL_TIMETABLES_USERS =

Liste aller E-Mail-Adressen von Nutzer*innen, die alle Stundenpläne sehen können sollen

[]
CAN_MANAGE_SALZH_USERS =
[]
CAN_UPLOAD_VPLAN_USERS =
[]
CAN_UPLOAD_FILES_USERS =
[]
CAN_MANAGE_NEWS_USERS =
[]
CAN_MANAGE_MONITORS_USERS =
[]
CAN_MANAGE_TABLETS_USERS =
[]
SV_USERS =

SV (Schülervertretung) kann:

  • Nachrichten an SuS schreiben

  • Umfragen unter SuS starten

  • Abstimmungen starten

[]
CAN_MANAGE_AGR_APP =
[]
CAN_MANAGE_BIB =
[]
CAN_MANAGE_BIB_SPECIAL_ACCESS =
[]
CAN_MANAGE_BIB_MEMBERS =
[]
SCHOOL_WEBSITE_API_URL =
''
DEVELOPMENT_MAIL_DELIVERY_POSITIVE_LIST =
[]
UNTIS_VERTRETUNGSPLAN_BASE_URL =
'https://beispielschule.de/vertretungsplan_lehrer'
UNTIS_VERTRETUNGSPLAN_USERNAME =
nil
UNTIS_VERTRETUNGSPLAN_PASSWORD =
nil
nil
CAN_REPORT_TECH_PROBLEMS_USERS =
[]
TECHNIKTEAM =

Liste aller E-Mail-Adressen von Nutzer*innen, die Tablet-Buchungen anderer einsehen und aufheben können, Technikprobleme melden und verwalten können und die Aula Tools nutzen können.

[]
TR =
{'String' => 'string',
  'Array' => 'list',
  'Hash' => 'hash',
  'TrueClass' => 'true',
  'FalseClass' => 'false',
  'NilClass' => 'null',
  'Integer' => 'int',
  'Float' => 'float'
}
CONFIG =
YAML::load_file('/data/config.yaml')
TIMETABLE_JSON_KEYS =
{
    # 6 => [:klasse, :stunde, :fach, :raum, :lehrer, :text],
    6 => [:stunde, :klasse, :lehrer, :raum, :fach, :text],
    7 => [:vnr, :stunde, :klasse, :lehrer, :raum, :fach, :text],
}
PING_TIME =
DEVELOPMENT ? 1 : 1
MAIL_FORWARDER_SLEEP =
DEVELOPMENT ? 60 : 60
MAIL_FORWARD_BATCH_SIZE =
30
SRSLY =
ARGV.include?('--srsly')
DELAYED_UPDATE_TIME =
DEVELOPMENT ? 0 : 0
SKIP_COLLECT_DATA =
true
DEBUG_ARCHIVE_PATH =
'/data/debug_archives/2023-07-23.zip'
SHARE_ARCHIVED_FILES =
ARGV.include?('--share-archived')
SHARE_SOURCE_FOLDER =
SHARE_ARCHIVED_FILES ? 'Unterricht-22-23' : 'Unterricht'
SHARE_TARGET_FOLDER =
SHARE_ARCHIVED_FILES ? 'Archiv-Jahresbeginn-23-24' : 'Unterricht'
ALSO_SHARE_OS_FOLDERS =
true
SHARE_READ =
1
SHARE_UPDATE =
2
SHARE_CREATE =
4
SHARE_DELETE =
8
SHARE_SHARE =
16
HTTP_READ_TIMEOUT =
60 * 10
ALSO_CREATE_OS_FOLDERS =
true
BASE62_ALPHABET =
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')
SEKRETARIAT_EMAIL =
'sekretariat@beispielschule.de'
VERTEILER_TEST_EMAILS =
["verteiler.test@#{SCHUL_MAIL_DOMAIN}"]
MAX_LOGIN_TRIES =
3
MATRIX_ADMIN_USER =
nil
MATRIX_ADMIN_PASSWORD =
nil
MATRIX_CORPORAL_CALLBACK_BEARER_TOKEN =
'bitte_jedes_bearer_token_nur_einmal_verwenden'
JWT_APPAUD_STREAM =
'stream'
JWT_APPKEY_STREAM =
'ein_langer_langer_richtig_langer_app_key'
JWT_DOMAIN_STREAM =
'.beispielschule.de'
JWT_APPAUD_AGRAPP =
'agrapp'
JWT_APPISS_AGRAPP =
'dashboard'
STREAM_SITE_URL =
'https://info.beispielschule.de/'
JITSI_ALL_ROOMS_URL =

der alle Räume und Teilnehmer im JSON-Format zurückgibt.

nil
SMS_PHONE_NUMBER_PASSPHRASE =
'bitte_ein_zufälliges_secret_generieren'
SMS_AUTH_UNLOCKED_FOR =
nil
WECHSELUNTERRICHT_KLASSENSTUFEN =
[]
GROUP_AF_ICONS =
{
    '' => '🏠',
    'it' => '🇮🇹',
    'gr' => '🇬🇷'
}
GROUP_AF_ICON_KEYS =
GROUP_AF_ICONS.keys.sort
STREAMING_TABLET_BOOKING_TIME_PRE =

tablet booking pre and post time, in minutes

5
STREAMING_TABLET_BOOKING_TIME_POST =
15
WANTS_TO_RECEIVE_TECHPOST_DEBUG_MAIL =

Liste aller E-Mail-Adressen von Nutzer*innen, die eine Debug-Mail erhalten möchten, wenn ein Nutzer ein Technikproblem meldet.

[]
CAN_MANAGE_AGS_USERS =
[]
CAN_MANAGE_ANTIKENFAHRT_USERS =
[]
CAN_MANAGE_BIB_PAYMENT =
[]
EXTERNAL_USERS =
[]
GEV_USERS =
[]
LEHRBUCHVEREIN_JAHR =
2023
TABLET_DEFAULT_COLOR =
'#d3d7cf'
SWITCH_WEEKS =

Definition von Wechselwochen

{'2021-03-08' => ['A', 2],
'2021-03-22' => nil}
nil
DEMO_ACCOUNT_EMAIL =
nil
DEMO_ACCOUNT_INFO =
nil
DEMO_ACCOUNT_FIXED_PIN =
nil
nil
[]
EXCLUDE_FROM_SELF_TEST_REPORT =
[]
ZEUGNISKONFERENZEN =
[]
CLING_COLORS =
[
  ["#ffe617", "daffodil"],
  ["#fad31c", "daisy"],
  ["#fdb717", "mustard"],
  ["#faaa21", "circus zest"],
  ["#f1753f", "pumpkin"],
  ["#ed5724", "tangerine"],
  ["#ef4538", "salmon"],
  ["#ea2830", "persimmon"],
  ["#bc2326", "rouge"],
  ["#8c0c03", "scarlet"],
  ["#e5185d", "hot pink"],

  ["#f384ae", "princess"],
  ["#fac6d2", "petal"],
  ["#b296c7", "lilac"],
  ["#7b67ae", "lavender"],
  ["#5f3577", "violet"],

  ["#c1d18a", "ceadon"],
  ["#799155", "olive"],
  ["#80bc42", "bamboo"],
  ["#4aa03f", "grass"],
  ["#16884a", "kelly"],
  ["#003f2e", "forrest"],

  ["#c3def1", "cloud"],
  ["#55beed", "dream"],
  ["#31a8e0", "gulf"],
  ["#238acc", "turquoise"],
  ["#0d60ae", "sky"],
  ["#143b86", "indigo"],
  ["#001b4a", "navy"],
  ["#7dcdc2", "sea foam"],
  ["#00a8a8", "teal"],
  ["#12959f", "peacock"],
  ["#094e54", "cyan"],

  ["#381e11", "chocolate"],
  ["#c05c20", "terra cotta"],
  ["#bf9b6b", "camel"],
  ["#e9d4a7", "linen"],
  ["#e7e6e1", "stone"],
  ["#cfd0d2", "smoke"],
  ["#8a8b8f", "steel"],
  ["#778590", "slate"],
  ["#474d4d", "charcoal"],
  ["#050608", "black"],
  ["#ffffff", "white"],
]
DRY_RUN =
__DRY_RUN__
DASH =
''
NAME_MATCH_STOP_WORDS =
Set.new(['de', 'la', 'von'])
BASE_DIR =
File.join(NEXTCLOUD_DASHBOARD_DATA_DIRECTORY, 'files', 'Unterricht')
PRESENCE_TOKEN_EXPIRY_TIME =

discard presence token after 600 seconds unless it's renewed in the meantime

60 * 10
LOGIN_METHODS =
{:email => 'Code per E-Mail', :sms => 'Code per SMS', :otp => 'OTP-Code'}
LOGIN_METHODS_SHORT =
{:email => 'E-Mail', :sms => 'SMS', :otp => 'OTP'}
SALZH_MODE_COLORS =
{:contact_person => 'warning', :salzh => 'danger', :hotspot_klasse => 'pink'}
SALZH_MODE_ICONS =
{:contact_person => 'fa-exclamation', :salzh => 'fa-home', :hotspot_klasse => 'fa-fire'}
SALZH_MODE_LABEL =
{:contact_person => 'Kontaktperson', :salzh => 'saLzH', :hotspot_klasse => 'Hotspot-Klasse'}
CW_TR =

include Magick

[
    ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..",
    ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-",
    ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."
]
TABLET_SET_WARNING_BEFORE_MINUTES =

TODO: move these to credentials

15
TABLET_SET_WARNING_AFTER_MINUTES =
15
COLOR_SCHEME_COLORS =
[
    ['l788aa3e8a598b56576', 'Dreamy'],
    ['lab8bbfa27776ab8bbe', 'Einfacher Plan', 'Thanos'],
    ['l7146749f6976cc8b79', 'Ice cream', 'Charlie'],
    ['le8a598b5657600a896', 'Pearly Purple'],
    ['l94b2a1ff7d03e0ff03', 'Orangensaft', 'Karla'],
    ['lc0c0c0ff6700ebebeb', 'Orange Justice'],
    ['d013adedf3a01f7fe2e', 'Sunset', 'Josefine'],
    ['lfcbf499e0001eeba30', 'Gryffindor'],
    ['dfad31c8c0c03381e11', 'Exotherm'],
    ['d6a00009626006a0000', 'Blutroter Sommer', 'Thanos'],
    ['d994500663300000000', 'Sandsturm'],
    ['l234567890123456789', 'Sonnenuntergang', 'Leo'],
    ['ldf0174ff0040ffffff', 'Kirscheis', 'Seraphine'],
    ['l32031ba63570ff99cc', 'BOOMBAYAH'],
    ['lffcc00c20aa10a9ec2', 'Unicorn'],
    ['l55beedf9b935e5185d', 'ソフトクリーム'],
    ['la86fd07638a15a2b7a', 'Amethyst'],
    ['lcc1ccca66aaab4bbbb', 'Lavendel', 'Leo'],
    ['l0b2f3ad0a9f5f8e0f7', 'Passive Night', 'Jack'],
    ['d0000ff6600ffcc00ff', 'Purple Valley', '𝕺𝕲 𝕭𝖎𝖌'],
    ['l073b4c118ab2946b2d', 'Ravenclaw'],
    ['la2c6e80d60aea2c6e8', 'Sky'],
    ['l000000013adfced8f8', 'In the refrigerator', 'Josefine'],
    ['d053a4d001eff00d7ff', 'Unter dem Meer', 'Linus'],
    ['d00ff000b0b6100ff00', 'Kristallmine', 'Josefine'],
    ['d401364131364799d54', 'Himmel Lila', 'HoloOmar10'],
    ['l307fdc03396cfe0e8d', 'Zoomer'],
    ['l12959f094e54e7eff6', 'Wellenbad'],
    ['d119c7503626414a19e', '20.000 Meilen unter dem Meer', 'Astrid'],
    ['d160520069960025061', 'Tiefsee'],
    ['d043402138610043402', 'Polarnacht', 'Astrid'],
    ['d4aa03f003f2e80bc42', 'Thylakoid'],
    ['l2a9134054a29e7ecef', 'Slytherin'],
    ['ldf01010b6121a9f5bc', 'Watermelon'],
    ['lf7fe2e088a08fa5882', 'Kaktus'],
    ['le8e33b6ca705f8f8df', 'Zitronen & Limetten', 'Astrid'],
    ['lceecf5f3f781f7819f', 'Dream', 'Jack'],
    ['daaaaaa777777cccccc', 'Shadows', 'Leo'],
    ['l0c0f0a463f3affbd00', 'Hufflepuff'],
    ['l9afe2e1c1c1c3c74c7', 'Libelle', 'Elias'],
    ['d290040232323290040', 'Aschgrauer Himmel', 'Thanos'],
    ['d151131222526333383', 'Midnight Sky', 'Leo'],
    ['d00f7000a0a0a00f700', 'H4CK3Я', 'Karla'],
    ['d990000000000000099', 'Spooner'],
    ['d000099000000009900', 'FreeRot']
]
STANDARD_COLOR_SCHEME =
'la2c6e80d60aea2c6e80'

Instance Method Summary collapse

Instance Method Details

#assert(condition, message = 'assertion failed', suppress_backtrace = false, delay = nil) ⇒ Object



26
27
28
29
30
31
32
33
34
# File 'src/ruby/vplan-watcher.rb', line 26

def assert(condition, message = 'assertion failed', suppress_backtrace = false, delay = nil)
    unless condition
        STDERR.puts message
        e = StandardError.new(message)
        e.set_backtrace([]) if suppress_backtrace
        sleep delay unless delay.nil?
        raise e
    end
end

#color_palette_for_color_scheme(color_scheme) ⇒ Object



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'src/ruby/create-cling-color-palette.rb', line 170

def color_palette_for_color_scheme(color_scheme)
    primary_color = '#' + color_scheme[7, 6]
    light = luminance(primary_color) > 160
    primary_color_darker = darken(primary_color, 0.8)
    desaturated_color = darken(desaturate(primary_color), 0.9)
    if light
        desaturated_color = rgb_to_hex(mix(hex_to_rgb(desaturate(primary_color)), hex_to_rgb('#ffffff'), 0.1))
    end
    desaturated_color_darker = darken(desaturate(primary_color), 0.3)
    disabled_color = light ? rgb_to_hex(mix(hex_to_rgb(primary_color), [255, 255, 255], 0.5)) : rgb_to_hex(mix(hex_to_rgb(primary_color), [192, 192, 192], 0.5))
    darker_color = rgb_to_hex(mix(hex_to_rgb(primary_color), [0, 0, 0], 0.6))
    shifted_color = shift_hue(primary_color, 350)
    main_text_color = light ? rgb_to_hex(mix(hex_to_rgb(primary_color), [0, 0, 0], 0.7)) : rgb_to_hex(mix(hex_to_rgb(primary_color), [255, 255, 255], 0.8))
    contrast_color = rgb_to_hex(mix(hex_to_rgb(primary_color), color_scheme[0] == 'l' ? [0, 0, 0] : [255, 255, 255], 0.7))
    color_palette = {
        :is_light => light,
        :primary => primary_color, 
        :primary_color_darker => primary_color_darker,
        :disabled => disabled_color, 
        :darker => darker_color, 
        :shifted => desaturated_color,
        :left => '#' + color_scheme[1, 6],
        :right => '#' + color_scheme[13, 6],
        :main_text => main_text_color,
        :contrast => contrast_color
    }
    color_palette
end

#cw_pattern(word) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'src/ruby/include/cypher.rb', line 12

def cw_pattern(word)
    pattern = ''
    word.each_char do |c|
        c = c.upcase
        ci = c.ord - 'A'.ord
        if c == ' '
            pattern += '       '
            while pattern.length % 8 != 0
                pattern += ' '
            end
        else
            cw = CW_TR[ci]
            (0...cw.size).each do |cwi|
                if cw[cwi] == '.'
                    pattern += '.'
                else
                    pattern += '...'
                end
                if cwi < cw.size - 1
                    pattern += ' '
                else
                    pattern += '   '
                end
            end
        end
    end
    pattern
end

#darken(c, f = 0.2) ⇒ Object



146
147
148
149
150
# File 'src/ruby/create-cling-color-palette.rb', line 146

def darken(c, f = 0.2)
    hsv = rgb_to_hsv(hex_to_rgb(c))
    hsv[2] *= f
    rgb_to_hex(hsv_to_rgb(hsv))
end

#debug(message, index = 0) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'src/ruby/main.rb', line 105

def debug(message, index = 0)
    index = 0
    begin
        while index < caller_locations.size - 1 && ['transaction', 'neo4j_query', 'neo4j_query_expect_one'].include?(caller_locations[index].base_label)
            index += 1
        end
    rescue
        index = 0
    end
    # STDERR.puts caller_locations.to_yaml
    l = caller_locations[index]
    ls = ''
    begin
        ls = "#{l.path.sub('/app/', '')}:#{l.lineno} @ #{l.base_label}"
    rescue
        ls = "#{l[0].sub('/app/', '')}:#{l[1]}"
    end
    STDERR.puts "#{DateTime.now.strftime('%H:%M:%S')} [#{ls}] #{message}"
end

#debug_error(message) ⇒ Object



125
126
127
128
129
130
131
132
133
134
# File 'src/ruby/main.rb', line 125

def debug_error(message)
    l = caller_locations.first
    ls = ''
    begin
        ls = "#{l.path.sub('/app/', '')}:#{l.lineno} @ #{l.base_label}"
    rescue
        ls = "#{l[0].sub('/app/', '')}:#{l[1]}"
    end
    STDERR.puts "#{DateTime.now.strftime('%H:%M:%S')} [ERROR] [#{ls}] #{message}"
end

#deliver_mail(plain_text = nil, &block) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'src/ruby/main.rb', line 180

def deliver_mail(plain_text = nil, &block)
    mail = Mail.new do
        charset = 'UTF-8'
        message = self.instance_eval(&block)
        if plain_text.nil?
            html_part do
                content_type 'text/html; charset=UTF-8'
                body message
            end

            text_part do
                content_type 'text/plain; charset=UTF-8'
                body mail_html_to_plain_text(message)
            end
        else
            text_part do
                content_type 'text/plain; charset=UTF-8'
                body plain_text
            end
        end
    end
    if DEVELOPMENT
        if DEVELOPMENT_MAIL_DELIVERY_POSITIVE_LIST.include?(mail.to.first)
            debug "Sending mail to #{mail.to.join(' / ')} because first recipient is included in DEVELOPMENT_MAIL_DELIVERY_POSITIVE_LIST..."
            mail.deliver!
        else
            debug "Not sending mail to because we're in development: #{mail.subject} => #{mail.to.join(' / ')}"
            debug mail.to_s
        end
    else
        mail.deliver!
    end
end

#desaturate(c) ⇒ Object



133
134
135
136
137
138
# File 'src/ruby/create-cling-color-palette.rb', line 133

def desaturate(c)
    hsv = rgb_to_hsv(hex_to_rgb(c))
    hsv[1] *= 0.7
    hsv[2] *= 0.9
    rgb_to_hex(hsv_to_rgb(hsv))
end

#find_available_target_paths(target_filename, cp_target_dir, mv_target_dir) ⇒ Object



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'src/ruby/collect-nextcloud-files-from-sus.script.rb', line 22

def find_available_target_paths(target_filename, cp_target_dir, mv_target_dir)
    n = 1
    while true do
        temp_target_filename = target_filename.dup
        if n > 1
            temp_parts = target_filename.split('.')
            inject_index = temp_parts.size - 2
            inject_index = 0 if inject_index < 0
            temp_parts[inject_index] += " (#{n})"
            temp_target_filename = temp_parts.join('.')
        end
        cp_path = File.join(cp_target_dir, temp_target_filename)
        mv_path = File.join(mv_target_dir, temp_target_filename)
        unless File.exist?(cp_path) || File.exist?(mv_path)
            return cp_path, mv_path
        end
        n += 1
    end
end

#fix_h_to_hh(s) ⇒ Object



136
137
138
139
140
141
142
143
# File 'src/ruby/main.rb', line 136

def fix_h_to_hh(s)
    return nil if s.nil?
    if s =~ /^\d:\d\d$/
        '0' + s
    else
        s
    end
end

#get_file_from_url(url, &block) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'src/ruby/vplan-watcher.rb', line 241

def get_file_from_url(url, &block)
    # STDERR.puts "Getting #{url}..."
    uri = URI.parse(url)
    req = Net::HTTP::Get.new(uri)
    if UNTIS_VERTRETUNGSPLAN_USERNAME && UNTIS_VERTRETUNGSPLAN_PASSWORD
        req.basic_auth UNTIS_VERTRETUNGSPLAN_USERNAME, UNTIS_VERTRETUNGSPLAN_PASSWORD
    end

    res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') do |http|
        response = http.request(req)
        if response.code.to_i == 200
            body = response.body
            body.force_encoding('iso-8859-1')
            body = body.encode('utf-8')
            yield(response.header, body)
        else
            STDERR.puts "WARNING: page not found: #{url}"
            # raise "page not found: #{url}"
        end
    end
end

#get_gradient(colors, t) ⇒ Object



160
161
162
163
164
165
166
167
168
# File 'src/ruby/create-cling-color-palette.rb', line 160

def get_gradient(colors, t)
    i = (t * (colors.size - 1)).to_i
    i = colors.size - 2 if i == colors.size - 1
    f = (t * (colors.size - 1)) - i
    f1 = 1.0 - f
    a = html_to_rgb(colors[i])
    b = html_to_rgb(colors[i + 1])
    rgb_to_html([a[0] * f1 + b[0] * f, a[1] * f1 + b[1] * f, a[2] * f1 + b[2] * f])
end

#handle_html_batch(bodies) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'src/ruby/vplan-watcher.rb', line 203

def handle_html_batch(bodies)
    datum_list = Set.new()
    removed_days = Set.new()
    bodies.each do |body|
        temp = handle_vplan_html_file(body, removed_days)
        if temp
            datum_list |= temp
        end
    end
    datum_list.each do |datum|
        File.open("/vplan/#{datum}.json", 'w') do |fout|
            data = {:entries => {}, :entry_ref => {}, :timetables => {}}
            Dir["/vplan/#{datum}/*.json"].each do |path|
                temp = JSON.parse(File.read(path))
                (temp['entries'] || []).each do |sha1|
                    data[:entries][sha1] = JSON.parse(File.read("/vplan/#{datum}/entries/#{sha1}.json"))
                end
                (temp['day_messages'] || []).each do |sha1|
                    data[:entries][sha1] = JSON.parse(File.read("/vplan/#{datum}/entries/#{sha1}.json"))
                end
            end
            Dir["/vplan/#{datum}/*.json"].each do |path|
                id = File.basename(path).sub('.json', '')
                entry = JSON.parse(File.read(path))
                unless entry.empty?
                    data[:timetables][id] = entry
                    (entry['entries'] || []).each do |e|
                        data[:entry_ref][e] ||= []
                        data[:entry_ref][e] << id
                    end
                end
            end
            fout.write(data.to_json)
        end
    end
    # trigger_update('all')
end

#handle_vplan_html_file(contents, removed_days) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'src/ruby/vplan-watcher.rb', line 72

def handle_vplan_html_file(contents, removed_days)
    dom = Nokogiri::HTML.parse(contents)
    dom = strip_past_tables(dom)
    return if dom.at_css('h2').nil?
    return if dom.at_css('#vertretung').nil?
    heading = dom.at_css('h2').text
    heading.gsub!('8?', '8o')
    heading.gsub!('9?', '9o')
    heading.gsub!('J11', '11')
    heading.gsub!('J12', '12')
    klasse = heading.split(' ').first
    if KLASSEN_ORDER.include?(klasse)
        heading = klasse
    end
    datum_list = Set.new()
    datum = nil
    result = {}
    dom.at_css('#vertretung').children.each do |child|
        if child.name == 'table' && datum
            table_mode = nil
            classes = child.attribute('class').to_s.split(' ')
            # STDERR.puts "[#{heading}] [#{datum}] [#{classes.join(' ')}]"
            if classes.include?('subst')
                # STDERR.print "(Vertretungsplan)"
                table_mode = :vplan
            else
                if child.css('tr').first.text == 'Nachrichten zum Tag'
                    # STDERR.print "(Nachrichten zum Tag)"
                    table_mode = :day_message
                else
                    # STDERR.puts "classes: #{classes.to_json}"
                    # STDERR.puts child.to_s
                    raise 'unexpected table'
                end
            end
            assert(!table_mode.nil?)
            # STDERR.puts child.to_s
            child.css('tr').each do |row|
                row.search('s').each do |n|
                    n.content = "__STRIKE_BEGIN__#{n.content}__STRIKE_END__"
                end
                row.search('br').each do |n|
                    n.content = "__LINE_BREAK__"
                end
                row.search('td/*').each do |n|
                    n.replace(n.content) unless n.name == 's'
                end

                tr = row.css('th')
                if tr.size == 6
                    # Klassenvertretungsplan:
                    headings = tr.map { |x| x.text }.join(' / ')
                    # assert(headings == 'Klasse(n) / Stunde / Fach / Raum / (Lehrer) / Text')
                    assert(headings == 'Stunde / Klasse(n) / (Lehrer) / (Raum) / (Fach) / Text')
                elsif tr.size == 7
                    # Lehrervertretungsplan: Vtr-Nr.	Stunde	Klasse(n)	(Lehrer)	(Raum)	(Fach)	Text
                    headings = tr.map { |x| x.text }.join(' / ')
                    assert(headings == 'Vtr-Nr. / Stunde / Klasse(n) / (Lehrer) / (Raum) / (Fach) / Text')
                end
                cells = row.css('td')
                if cells.size == 1 && table_mode == :day_message
                    result[datum] ||= {}
                    day_message = cells.first.text.gsub('__LINE_BREAK__', "\n").strip
                    sha1 = Digest::SHA1.hexdigest(day_message.to_json)[0, 8]
                    path = "/vplan/#{datum}/entries/#{sha1}.json"
                    FileUtils.mkpath(File.dirname(path))
                    File.open(path, 'w') { |f| f.write(day_message.to_json) }
                    result[datum][:day_messages] ||= []
                    result[datum][:day_messages] << sha1
                elsif (cells.size == 6 || cells.size == 7) && table_mode == :vplan
                    # Klassenvertretungsplan: Klasse(n)	Stunde	Fach	Raum	(Lehrer)	Text
                    # Lehrervertretungsplan: Vtr-Nr.	Stunde	Klasse(n)	(Lehrer)	(Raum)	(Fach)	Text
                    result[datum] ||= {}
                    result[datum][:entries] ||= []
                    entry = {}
                    cells.each.with_index do |x, index|
                        key = TIMETABLE_JSON_KEYS[cells.size][index]
                        text = x.content || ''
                        # replace &nbsp; with normal space and strip
                        text.gsub!(/[[:space:]]+/, ' ')
                        text.strip!
                        entry_del = nil
                        entry_add = nil
                        if text.include?('__STRIKE_BEGIN__')
                            text.gsub!('__STRIKE_BEGIN__', '')
                            parts = text.split('__STRIKE_END__')
                            entry_del = (parts[0] || '').strip
                            entry_add = (parts[1] || '').strip
                        else
                            entry_add = (text || '').strip
                        end
                        entry_add = nil if entry_add && entry_add.empty?
                        entry_del = nil if entry_del && entry_del.empty?
                        entry_add = entry_add[1, entry_add.size - 1] if entry_add && entry_add[0] == '?'
                        entry[key] = [entry_del, entry_add]
                    end
                    fixed_entry = [entry[:stunde][1], entry[:klasse], entry[:lehrer], entry[:fach], entry[:raum], entry[:text][1]]
                    sha1 = Digest::SHA1.hexdigest(fixed_entry.to_json)[0, 8]
                    path = "/vplan/#{datum}/entries/#{sha1}.json"
                    FileUtils.mkpath(File.dirname(path))
                    File.open(path, 'w') { |f| f.write(fixed_entry.to_json) }
                    result[datum][:entries] << sha1
                end
                # STDERR.print " #{cells.size}"
            end
            # STDERR.puts
            # STDERR.puts '-' * 40
        else
            b = nil
            b = child.text if child.name == 'b'
            child.css('b').each { |c2| b = c2.text }
            if b
                datum = parse_html_datum(b)
                datum_list << datum
                unless removed_days.include?(datum)
                    Dir["/vplan/#{datum}/*.json"].each do |path|
                        FileUtils::rm_f(path)
                    end
                    removed_days << datum
                end
            end
        end
    end
    result.each_pair do |datum, info|
        path = "/vplan/#{datum}/#{heading.gsub('/', '-')}.json"
        FileUtils.mkpath(File.dirname(path))
        File.open(path, 'w') { |f| f.write(result[datum].to_json) }
    end
    return datum_list
end

#head_file_from_url(url, &block) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'src/ruby/vplan-watcher.rb', line 263

def head_file_from_url(url, &block)
    uri = URI.parse(url)
    req = Net::HTTP::Head.new(uri)
    if UNTIS_VERTRETUNGSPLAN_USERNAME && UNTIS_VERTRETUNGSPLAN_PASSWORD
        req.basic_auth UNTIS_VERTRETUNGSPLAN_USERNAME, UNTIS_VERTRETUNGSPLAN_PASSWORD
    end
    
    res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') do |http|
        response = http.request(req)
        if response.code.to_i == 200
            yield(response.header)
        else
            raise "page not found: #{url}"
        end
    end
end

#hex_to_rgb(c) ⇒ Object



110
111
112
113
114
115
# File 'src/ruby/create-cling-color-palette.rb', line 110

def hex_to_rgb(c)
    r = c[1, 2].downcase.to_i(16)
    g = c[3, 2].downcase.to_i(16)
    b = c[5, 2].downcase.to_i(16)
    [r, g, b]
end

#hsv_to_rgb(c) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'src/ruby/create-cling-color-palette.rb', line 57

def hsv_to_rgb(c)
    h, s, v = c[0].to_f / 360, c[1].to_f / 100, c[2].to_f / 100
    h_i = (h * 6).to_i
    f = h * 6 - h_i
    p = v * (1 - s)
    q = v * (1 - f * s)
    t = v * (1 - (1 - f) * s)
    r, g, b = v, t, p if h_i == 0
    r, g, b = q, v, p if h_i == 1
    r, g, b = p, v, t if h_i == 2
    r, g, b = p, q, v if h_i == 3
    r, g, b = t, p, v if h_i == 4
    r, g, b = v, p, q if h_i == 5
    [(r * 255).to_i, (g * 255).to_i, (b * 255).to_i]
end

#html_to_rgb(x) ⇒ Object



152
153
154
# File 'src/ruby/create-cling-color-palette.rb', line 152

def html_to_rgb(x)
    [x[1, 2].to_i(16), x[3, 2].to_i(16), x[5, 2].to_i(16)]
end

#i_to_b62(n) ⇒ Object



5
6
7
8
9
10
11
12
13
# File 'src/ruby/background-renderer.rb', line 5

def i_to_b62(n)
    s = ''
    while n > 0
        d = n % 62
        s = BASE62_ALPHABET[d] + s
        n /= 62
    end
    s
end

#join_with_sep(list, a, b) ⇒ Object



220
221
222
# File 'src/ruby/main.rb', line 220

def join_with_sep(list, a, b)
    list.size == 1 ? list.first : [list[0, list.size - 1].join(a), list.last].join(b)
end

#luminance(color) ⇒ Object



117
118
119
120
# File 'src/ruby/create-cling-color-palette.rb', line 117

def luminance(color) 
    r, g, b = hex_to_rgb(color)
    return r * 0.299 + g * 0.587 + b * 0.114
end

#mail_html_to_plain_text(s) ⇒ Object



176
177
178
# File 'src/ruby/main.rb', line 176

def mail_html_to_plain_text(s)
    s.gsub('<p>', "\n\n").gsub(/<br\s*\/?>/, "\n").gsub(/<\/?[^>]*>/, '').strip
end

#mix(a, b, t) ⇒ Object



122
123
124
125
126
127
# File 'src/ruby/create-cling-color-palette.rb', line 122

def mix(a, b, t)
    t1 = 1.0 - t
    return [a[0] * t1 + b[0] * t,
            a[1] * t1 + b[1] * t,
            a[2] * t1 + b[2] * t]
end

#override_email_login_recipient_for_chat(email) ⇒ Object



254
255
256
# File 'src/ruby/credentials.template.rb', line 254

def (email)
    email
end

#parse_html_datum(s) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'src/ruby/vplan-watcher.rb', line 36

def parse_html_datum(s)
    parts = s.split('.')
    d = parts[0].to_i
    m = parts[1].to_i
    _ = CONFIG[:first_school_day][0, 4].to_i
    (_ .. (_ + 1)).each do |y|
        ds = sprintf('%04d-%02d-%02d', y, m, d)
        if ds >= CONFIG[:first_school_day] && ds <= CONFIG[:last_day]
            return ds
        end
    end
    raise 'nope'
end

#parse_markdown(s) ⇒ Object



214
215
216
217
218
# File 'src/ruby/main.rb', line 214

def parse_markdown(s)
    s ||= ''
    s.gsub!(/\w\*in/) { |x| x.sub('*', '\\*') }
    Kramdown::Document.new(s, :smart_quotes => %w{sbquo lsquo bdquo ldquo}).to_html.strip
end

#perform_refreshObject



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'src/ruby/vplan-watcher.rb', line 280

def perform_refresh
    last_update_timestamp = ''
    last_update_timestamp_path = '/vplan/timestamp.txt'
    if File.exist?(last_update_timestamp_path)
        last_update_timestamp = File.read(last_update_timestamp_path).strip
    end
    last_modified = last_update_timestamp
    head_file_from_url("#{UNTIS_VERTRETUNGSPLAN_BASE_URL}/frames/navbar.htm") do |header|
        last_modified = DateTime.parse(header['last-modified']).strftime('%Y-%m-%d-%H-%M-%S')
    end
    return unless last_modified > last_update_timestamp

    STDERR.puts "Fetching vplan from #{UNTIS_VERTRETUNGSPLAN_BASE_URL}!"

    get_file_from_url("#{UNTIS_VERTRETUNGSPLAN_BASE_URL}/frames/navbar.htm") do |header, body|
        file_count = 0
        bodies = []
        dom = Nokogiri::HTML.parse(body)
        weeks = []
        dom.css('select').each do |element|
            if element.attr('name') == 'week'
                element.css('option').each do |option|
                    weeks << option.attr('value').to_i
                end
            end
        end
        classes = JSON.parse(body.match(/var classes\s*=\s*([^;]+);/)[1])
        teachers = JSON.parse(body.match(/var teachers\s*=\s*([^;]+);/)[1])
        weeks.each do |week|
            (0...teachers.size).each do |index|
                file_count += 1
                get_file_from_url("#{UNTIS_VERTRETUNGSPLAN_BASE_URL}/#{sprintf('%02d', week)}/v/#{sprintf('v%05d', index + 1)}.htm") do |header, body|
                    bodies << body
                end
            end
            (0...classes.size).each do |index|
                file_count += 1
                get_file_from_url("#{UNTIS_VERTRETUNGSPLAN_BASE_URL}/#{sprintf('%02d', week)}/w/#{sprintf('w%05d', index + 1)}.htm") do |header, body|
                    bodies << body
                end
            end
        end
        STDERR.puts "Fetched #{file_count} HTML files, parsing..."
        handle_html_batch(bodies)
    end
    File.open(last_update_timestamp_path, 'w') { |f| f.puts(last_modified) }
    STDERR.puts "Triggering timetable update..."
    system("curl -s http://timetable:8080/api/update/all")
end

#remove_accents(s) ⇒ Object



101
102
103
# File 'src/ruby/main.rb', line 101

def remove_accents(s)
    I18n.transliterate(s.gsub('ä', 'ae').gsub('ö', 'oe').gsub('ü', 'ue').gsub('Ä', 'Ae').gsub('Ö', 'Oe').gsub('Ü', 'Ue').gsub('ß', 'ss').gsub('ė', 'e').gsub('š', 's'))
end

#rgb_to_hex(c) ⇒ Object



129
130
131
# File 'src/ruby/create-cling-color-palette.rb', line 129

def rgb_to_hex(c)
    sprintf('#%02x%02x%02x', c[0].to_i, c[1].to_i, c[2].to_i)
end

#rgb_to_hsv(c) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'src/ruby/create-cling-color-palette.rb', line 75

def rgb_to_hsv(c)
    r = c[0] / 255.0
    g = c[1] / 255.0
    b = c[2] / 255.0
    max = [r, g, b].max
    min = [r, g, b].min
    delta = max - min
    v = max * 100

    if (max != 0.0)
        s = delta / max *100
    else
        s = 0.0
    end

    if (s == 0.0)
        h = 0.0
    else
        if (r == max)
            h = (g - b) / delta
        elsif (g == max)
            h = 2 + (b - r) / delta
        elsif (b == max)
            h = 4 + (r - g) / delta
        end

        h *= 60.0

        if (h < 0)
            h += 360.0
        end
    end
    [h, s, v]
end

#rgb_to_html(x) ⇒ Object



156
157
158
# File 'src/ruby/create-cling-color-palette.rb', line 156

def rgb_to_html(x)
    sprintf('#%02x%02x%02x', x[0], x[1], x[2])
end

#shift_hue(c, f = 60) ⇒ Object



140
141
142
143
144
# File 'src/ruby/create-cling-color-palette.rb', line 140

def shift_hue(c, f = 60)
    hsv = rgb_to_hsv(hex_to_rgb(c))
    hsv[0] = (hsv[0] + f) % 360.0
    rgb_to_hex(hsv_to_rgb(hsv))
end

#strip_past_tables(dom) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'src/ruby/vplan-watcher.rb', line 50

def strip_past_tables(dom)
    dom.css('td').each do |td|
        if (td.text || '').strip == 'Vertretungen sind nicht freigegeben'
            n = td.parent.parent
            deleted_table = false
            while n != nil
                p = n.previous
                n.remove()
                if n && n.name == 'table'
                    if deleted_table
                        break
                    else
                        deleted_table = true
                    end
                end
                n = p
            end
        end
    end
    dom
end