code:
[Global]
IconName=yakuake
Comment=Yakuake
Comment[bg]=Yakuake
Comment[ca]=Yakuake
Comment[ca@valencia]=Yakuake
Comment[cs]=Yakuake
Comment[da]=Yakuake
Comment[de]=Yakuake
Comment[el]=Yakuake
Comment[es]=Yakuake
Comment[et]=Yakuake
Comment[fi]=Yakuake
Comment[ga]=Yakuake
Comment[gl]=Yakuake
Comment[hu]=Yakuake
Comment[it]=Yakuake
Comment[km]=Yakuake
Comment[lt]=Yakuake
Comment[nb]=Yakuake
Comment[nds]=Yakuake
Comment[nl]=Yakuake
Comment[pa]=ਯਾਕੁਕੀ
Comment[pl]=Yakuake
Comment[pt]=Yakuake
Comment[pt_BR]=Yakuake
Comment[ro]=Yakuake
Comment[sk]=Yakuake
Comment[sr]=Јакуаке
Comment[sr@ijekavian]=Јакуаке
Comment[sr@ijekavianlatin]=Yakuake
Comment[sr@latin]=Yakuake
Comment[sv]=Yakuake
Comment[tr]=Yakuake
Comment[ug]=Yakuake
Comment[uk]=Yakuake
Comment[x-test]=xxYakuakexx
Comment[zh_CN]=Yakuake
Comment[zh_TW]=Yakuake

[Event/startup]
Name=Startup
Name[ca]=Inici
Name[ca@valencia]=Inici
Name[cs]=Spuštění
Name[da]=Opstart
Name[de]=Programmstart
Name[el]=Εκκίνηση
Name[es]=Inicio
Name[et]=Käivitamine
Name[fi]=Käynnistys
Name[gl]=Inicio
Name[hu]=Indulás
Name[it]=Avvio
Name[km]=ចាប់ផ្ដើម
Name[lt]=Paleidimas
Name[nb]=Oppstart
Name[nds]=Start
Name[nl]=Opstarten
Name[pa]=ਸ਼ੁਰੂਆਤ
Name[pl]=Uruchamianie
Name[pt]=Arranque
Name[pt_BR]=Inicialização
Name[ro]=Pornire
Name[sk]=Spustenie
Name[sr]=Покретање
Name[sr@ijekavian]=Покретање
Name[sr@ijekavianlatin]=Pokretanje
Name[sr@latin]=Pokretanje
Name[sv]=Start
Name[tr]=Başlangıç
Name[ug]=قوزغىلىش
Name[uk]=Запуск
Name[x-test]=xxStartupxx
Name[zh_CN]=启动
Name[zh_TW]=啟動
Comment=Yakuake has started
Comment[ca]=S'ha engegat el Yakuake
Comment[ca@valencia]=S'ha engegat el Yakuake
Comment[cs]=Yakuake byl spuštěn
Comment[da]=Yakuake er startet
Comment[de]=Yakuake wurde gestartet
Comment[el]=Το Yakuake έχει ξεκινήσει
Comment[es]=Yakuake se ha iniciado
Comment[et]=Yakuake on käivitatud
Comment[fi]=Yakuake on käynnistynyt
Comment[gl]=O Yakuake foi iniciado
Comment[hu]=A Yakuake elindult
Comment[it]=Yakuake è avviato
Comment[km]=Yakuake បានចាប់ផ្ដើម
Comment[lt]=Yakuake paleistas
Comment[nb]=Yakuake har startet
Comment[nds]=Yakuake hett start.
Comment[nl]=Yakuake is opgestart
Comment[pa]=ਯਾਕੂਕੀ ਚਾਲੂ ਹੋਇਆ
Comment[pl]=Yakuake został uruchomiony
Comment[pt]=O Yakuake foi iniciado
Comment[pt_BR]=O Yakuake foi iniciado
Comment[ro]=Yakuake s-a pornit
Comment[sk]=Yakuake bolo spustené
Comment[sr]=Јакуаке је покренут
Comment[sr@ijekavian]=Јакуаке је покренут
Comment[sr@ijekavianlatin]=Yakuake je pokrenut
Comment[sr@latin]=Yakuake je pokrenut
Comment[sv]=Yakuake har startats
Comment[tr]=Yakuake başlatıldı
Comment[uk]=Yakuake запущено
Comment[x-test]=xxYakuake has startedxx
Comment[zh_CN]=Yakuake 已启动
Comment[zh_TW]=Yakuake 已啟動
Action=Popup

[Event/silence]
Name=Silence detected
Name[ca]=S'ha detectat silenci
Name[ca@valencia]=S'ha detectat silenci
Name[cs]=Detekováno ticho
Name[da]=Stilhed detekteret
Name[de]=Keine Aktivität festgestellt
Name[el]=Ανιχνεύθηκε αδράνεια
Name[es]=Silencio detectado
Name[et]=Tuvastati vaikus
Name[fi]=Hiljaisuutta havaittu
Name[gl]=Silencio detectado
Name[hu]=Csend érzékelve
Name[it]=Inattività rilevata
Name[km]=បានរកឃើញភាពស្ងាត់
Name[lt]=Aptikta tyla
Name[nb]=Stillhet oppdaget
Name[nds]=Still opdeckt
Name[nl]=Stilte ontdekt
Name[pa]=ਚੁੱਪ ਖੋਜਿਆ ਗਿਆ
Name[pl]=Wykryto ciszę
Name[pt]=Silêncio detectado
Name[pt_BR]=Silêncio detectado
Name[ro]=Tăcere detectată
Name[sk]=Detekované ticho
Name[sr]=Откривена тишина
Name[sr@ijekavian]=Откривена тишина
Name[sr@ijekavianlatin]=Otkrivena tišina
Name[sr@latin]=Otkrivena tišina
Name[sv]=Tystnad funnen
Name[tr]=Sessizlik belirlendi
Name[uk]=Виявлено бездіяльність
Name[x-test]=xxSilence detectedxx
Name[zh_CN]=探测到静默
Name[zh_TW]=偵測到沉默狀態
Comment=Silence detected in a monitored terminal or session
Comment[ca]=S'ha detectat silenci en un terminal vigilat o sessió
Comment[ca@valencia]=S'ha detectat silenci en un terminal vigilat o sessió
Comment[cs]=Detekováno ticho v monitorovaném terminálu nebo sezení
Comment[da]=Stilhed detekteret i en overvåget terminalsession
Comment[de]=In einem überwachten Terminal oder einer überwachten Sitzung wird keine Aktivität festgestellt
Comment[el]=Ανιχνεύθηκε αδράνεια σε ένα ελεγχόμενο τερματικό ή συνεδρία
Comment[es]=Se ha detectado silencio en una terminal o sesión monitoreada
Comment[et]=Jälgitavas terminalis või seansis tuvastati vaikus
Comment[fi]=Hiljaisuutta havaittu tarkkaillussa päätteessä tai istunnossa
Comment[gl]=Silencio detectado nunha terminal ou sesión monitorizada
Comment[hu]=Csend érzékelve egy figyelt terminálon vagy munkamenetben
Comment[it]=Inattività rilevata in un terminale o sessione monitorata
Comment[km]=បានរកឃើញភាពស្ងាត់នៅក្នុងសម័យ ឬស្ថានីយដែលបានត្រួតពិនិត្យ
Comment[lt]=Stebimoje terminale ar sesijoje pastebėta tyla
Comment[nb]=Det er oppdaget stillhet i en terminal eller økt som er overvåket
Comment[nds]=En beluurt Konsool oder Törn is still
Comment[nl]=Stilte ontdekt in een gevolgde terminal of sessie
Comment[pl]=Wykryto ciszę w monitorowanej sesji terminala
Comment[pt]=Silêncio detectado num terminal ou sessão vigiado
Comment[pt_BR]=Silêncio detectado em um terminal ou sessão monitorado
Comment[ro]=A fost detectată tăcere într-o sesiune sau terminal monitorizat
Comment[sk]=Detekované ticho v monitorovanom termináli alebo sedení
Comment[sr]=Откривена је тишина у надгледаном терминалу или сесији
Comment[sr@ijekavian]=Откривена је тишина у надгледаном терминалу или сесији
Comment[sr@ijekavianlatin]=Otkrivena je tišina u nadgledanom terminalu ili sesiji
Comment[sr@latin]=Otkrivena je tišina u nadgledanom terminalu ili sesiji
Comment[sv]=Tystnad funnen i en bevakad terminal eller session
Comment[tr]=İzlenen uçbirimde ya da oturumda sessizlik belirlendi
Comment[uk]=У сеансі або терміналі, за яким ведеться спостереження виявлено бездіяльність
Comment[x-test]=xxSilence detected in a monitored terminal or sessionxx
Comment[zh_CN]=在监视的终端或会话中探测到静默
Comment[zh_TW]=在一個監控中的終端機或工作階段偵測到沉默狀態
Action=Popup

[Event/activity]
Name=Activity detected
Name[ca]=S'ha detectat activitat
Name[ca@valencia]=S'ha detectat activitat
Name[cs]=Detekována aktivita
Name[da]=Aktivitet detekteret
Name[de]=Aktivität festgestellt
Name[el]=Ανιχνεύθηκε δραστηριότητα
Name[es]=Actividad detectada
Name[et]=Tuvastati aktiivsus
Name[fi]=Aktiivisuutta havaittu
Name[gl]=Actividade detectada
Name[hu]=Aktivitás észlelve
Name[it]=Attività rilevata
Name[km]=សកម្មភាពដែលបានរកឃើញ
Name[lt]=Aptikta veikla
Name[nb]=Aktivitet oppdaget
Name[nds]=Aktiviteet opdeckt
Name[nl]=Activiteit ontdekt
Name[pa]=ਐਕਟਵਿਟੀ ਖੋਜੀ ਗਈ
Name[pl]=Wykryto aktywność
Name[pt]=Actividade detectada
Name[pt_BR]=Atividade detectada
Name[ro]=Activitate detectată
Name[sk]=Detekovaná aktivita
Name[sr]=Откривена активност
Name[sr@ijekavian]=Откривена активност
Name[sr@ijekavianlatin]=Otkrivena aktivnost
Name[sr@latin]=Otkrivena aktivnost
Name[sv]=Aktivitet funnen
Name[tr]=Etkinlik belirlendi
Name[uk]=Виявлено діяльність
Name[x-test]=xxActivity detectedxx
Name[zh_CN]=探测到活动
Name[zh_TW]=偵測到活動
Comment=Activity detected in a monitored terminal or session
Comment[ca]=S'ha detectat activitat en un terminal vigilat o sessió
Comment[ca@valencia]=S'ha detectat activitat en un terminal vigilat o sessió
Comment[cs]=Detekována aktivita v monitorovaném terminálu nebo sezení
Comment[da]=Aktivitet detekteret i en overvåget terminal eller session
Comment[de]=In einem überwachten Terminal oder einer überwachten Sitzung wird Aktivität festgestellt
Comment[el]=Ανιχνεύθηκε δραστηριότητα σε ένα ελεγχόμενο τερματικό ή συνεδρία
Comment[es]=Se ha detectado actividad en una terminal o sesión monitoreada
Comment[et]=Jälgitavas terminalis või seansis tuvastati aktiivsus
Comment[fi]=Aktiivisuutta havaittu tarkkaillussa päätteessä tai istunnossa
Comment[gl]=Actividade detectada nunha terminal ou sesión monitorizada
Comment[hu]=Aktivitás érzékelve egy figyelt terminálon vagy munkamenetben
Comment[it]=Attività rilevata in un terminale o sessione monitorata
Comment[km]=សកម្មភាពដែលបានរកឃើញនៅក្នុងសម័យ ឬស្ថានីយដែលបានត្រួតពិនិត្យ
Comment[lt]=Stebimoje terminale ar sesijoje pastebėta veikla
Comment[nb]=Det er oppdaget aktivitet i en terminal eller økt som er overvåket
Comment[nds]=En beluurt Konsool oder Törn is Aktiviteet
Comment[nl]=Activiteit ontdekt in een gevolgde terminal of sessie
Comment[pl]=Wykryto aktywność w monitorowanej sesji terminala
Comment[pt]=Actividade detectada num terminal ou sessão vigiado
Comment[pt_BR]=Atividade detectada em um terminal ou sessão monitorado
Comment[ro]=A fost detectată activitate într-o sesiune sau terminal monitorizat
Comment[sk]=Detekovaná aktivita v monitorovanom termináli alebo sedení
Comment[sr]=Откривена је активност у надгледаном терминалу или сесији
Comment[sr@ijekavian]=Откривена је активност у надгледаном терминалу или сесији
Comment[sr@ijekavianlatin]=Otkrivena je aktivnost u nadgledanom terminalu ili sesiji
Comment[sr@latin]=Otkrivena je aktivnost u nadgledanom terminalu ili sesiji
Comment[sv]=Aktivitet funnen i en bevakad terminal eller session
Comment[tr]=İzlenen uçbirimde ya da oturumda etkinlik belirlendi
Comment[uk]=У сеансі або терміналі, за яким ведеться спостереження виявлено діяльність
Comment[x-test]=xxActivity detected in a monitored terminal or sessionxx
Comment[zh_CN]=在监视的终端或会话中探测到活动
Comment[zh_TW]=在一個監控中的終端機或工作階段偵測到有活動
Action=Popup

[Event/BellVisible]
Name=Bell in Visible Session
Name[bs]=Zvono u vidljivoj sesiji
Name[ca]=Timbre en una sessió visible
Name[cs]=Zvonek ve viditelném sezení
Name[da]=Bip i synlig session
Name[de]=Signalton in sichtbarer Sitzung
Name[el]=Ηχητικό σήμα σε ορατή συνεδρία
Name[et]=Heli nähtavas seansis
Name[fi]=Äänimerkki näkyvässä istunnossa
Name[ga]=Cloigín i Seisiún Infheicthe
Name[gl]=Badalada na sesión visíbel
Name[hu]=Csengő a látható munkamenetben
Name[it]=Campana in sessione visibile
Name[lt]=Skambutis matomoje sesijoje
Name[nb]=Varsel i synlig økt
Name[nl]=Geluidssignaal in zichtbare sessie
Name[nn]=Bjølle i synleg økt
Name[pl]=Dzwonek w widocznej sesji
Name[pt]=Campainha numa Sessão Visível
Name[pt_BR]=Campainha na sessão visível
Name[ro]=Clopoțel în sesiune vizibilă
Name[se]=Divga oainnus bargovuorus
Name[sk]=Zvonček v zobrazenom sedení
Name[sr]=Звоно у видљивој сесији
Name[sr@ijekavian]=Звоно у видљивој сесији
Name[sr@ijekavianlatin]=Zvono u vidljivoj sesiji
Name[sr@latin]=Zvono u vidljivoj sesiji
Name[sv]=Ljudsignal i synlig session
Name[uk]=Гудок у видимий сеанс
Name[x-test]=xxBell in Visible Sessionxx
Name[zh_CN]=可见会话中的响铃
Name[zh_TW]=可見工作階段響鈴
Comment=Bell emitted within a visible session
Comment[bs]=Oglašeno je zvono unutar vidljive sesije
Comment[ca]=Timbre emès en una sessió visible
Comment[cs]=Zvonek spuštěný ve viditelném sezení
Comment[da]=Bip udsendt indenfor en synlig session
Comment[de]=Signalton, der in einer sichtbaren Sitzung ertönt
Comment[el]=Ηχητικό σήμα εκπέμπεται σε ορατή συνεδρία
Comment[et]=Heli nähtavas seansis
Comment[fi]=Äänimerkki lähetetty näkyvässä istunnossa
Comment[ga]=Baineadh an cloigín i seisiún infheicthe
Comment[gl]=Badalada emitida nunha sesión visíbel
Comment[hu]=Csengetés a látható munkamenetben
Comment[it]=Campana emessa in una sessione visibile
Comment[lt]=Matomos sesijos metu skambutis neveiks
Comment[nb]=Signal sendt ut fra en synlig økt
Comment[nl]=Geluidssignaal aangeroepen in zichtbare sessie
Comment[nn]=Bjøllesignal sendt i ei synleg økt
Comment[pl]=Dzwonek uruchamiany w widocznej sesji
Comment[pt]=Campainha emitida dentro de uma sessão visível
Comment[pt_BR]=Campainha emitida dentro de uma sessão visível
Comment[ro]=Clopoțel emis în cadrul unei sesiuni vizibile
Comment[se]=Divgasignála sáddejuvvui oainnus bargovuorus
Comment[sk]=Zvonček poslaný v zobrazenom sedení
Comment[sr]=Емитовано је звоно унутар видљиве сесије.
Comment[sr@ijekavian]=Емитовано је звоно унутар видљиве сесије.
Comment[sr@ijekavianlatin]=Emitovano je zvono unutar vidljive sesije.
Comment[sr@latin]=Emitovano je zvono unutar vidljive sesije.
Comment[sv]=Ljudsignal avgiven inne i en synlig session
Comment[uk]=Звучить гудок у видимому сеансі
Comment[x-test]=xxBell emitted within a visible sessionxx
Comment[zh_CN]=可见会话中发生的响铃
Comment[zh_TW]=可見工作階段中的響鈴行為
Action=None

[Event/BellInvisible]
Name=Bell in Non-Visible Session
Name[bs]=Zvono u zaklonjenoj sesiji
Name[ca]=Timbre en una sessió no visible
Name[cs]=Zvonek ve skrytém sezení
Name[da]=Bip i ikke-synlig session
Name[de]=Signalton in nicht sichtbarer Sitzung
Name[el]=Ηχητικό σήμα σε μη ορατή συνεδρία
Name[et]=Heli nähtamatus seansis
Name[fi]=Äänimerkki näkymättömässä istunnossa
Name[ga]=Cloigín i Seisiún Dofheicthe
Name[gl]=Badalada nunha sesión non visíbel
Name[hu]=Csengő a nem látható munkamenetben
Name[it]=Campana in sessione non visibile
Name[lt]=Skambutis nematomoje sesijoje
Name[nb]=Varsel i ikke-synlig økt
Name[nl]=Geluidssignaal in niet-zichtbare sessie
Name[nn]=Bjølle i ikkje-synleg økt
Name[pl]=Dzwonek w niewidocznej sesji
Name[pt]=Campainha numa Sessão Invisível
Name[pt_BR]=Campainha na sessão não visível
Name[ro]=Clopoțel în sesiune nevizibilă
Name[se]=Divga oaidnemeahttun bargovuorus
Name[sk]=Zvonček v nezobrazenom sedení
Name[sr]=Звоно у сесији која није видљива
Name[sr@ijekavian]=Звоно у сесији која није видљива
Name[sr@ijekavianlatin]=Zvono u sesiji koja nije vidljiva
Name[sr@latin]=Zvono u sesiji koja nije vidljiva
Name[sv]=Ljudsignal i icke-synlig session
Name[uk]=Гудок у невидимому сеансі
Name[x-test]=xxBell in Non-Visible Sessionxx
Name[zh_CN]=不可见会话中的响铃
Name[zh_TW]=非可見工作階段的響鈴
Comment=Bell emitted within a non-visible session
Comment[bs]=Oglašeno je zvono unutar zaklonjene sesije
Comment[ca]=Timbre emès en una sessió no visible
Comment[cs]=Zvonek spuštěný ve skrytém sezení
Comment[da]=Bip udsendt indenfor en ikke-synlig session
Comment[de]=Signalton, der in einer nicht sichtbaren Sitzung ertönt
Comment[el]=Ηχητικό σήμα εκπέμπεται σε μη ορατή συνεδρία
Comment[et]=Heli nähtamatus seansis
Comment[fi]=Äänimerkki lähetetty näkymättömässä istunnossa
Comment[ga]=Baineadh an cloigín i seisiún dofheicthe
Comment[gl]=Badalada emitida nunha sesión non visíbel
Comment[hu]=Csengetés a nem látható munkamenetben
Comment[it]=Campana emessa in una sessione non visibile
Comment[lt]=Nematomos sesijos metu skambutis neveiks
Comment[nb]=Signal sendt ut i en økt som ikke er synlig
Comment[nl]=Geluidssignaal aangeroepen in niet-zichtbare sessie
Comment[nn]=Bjøllesignal sendt i ei ikkje-synleg økt
Comment[pl]=Dzwonek uruchamiany w niewidocznej sesji
Comment[pt]=Campainha emitida dentro de uma sessão não-visível
Comment[pt_BR]=Campainha emitida dentro de uma sessão não visível
Comment[ro]=Clopoțel emis în cadrul unei sesiuni nevizibile
Comment[se]=Divgasignála sáddejuvvui oaidnemeahttun bargovuorus
Comment[sk]=Zvonček poslaný v nezobrazenom sedení
Comment[sr]=Емитовано је звоно у сесији која није видљива.
Comment[sr@ijekavian]=Емитовано је звоно у сесији која није видљива.
Comment[sr@ijekavianlatin]=Emitovano je zvono u sesiji koja nije vidljiva.
Comment[sr@latin]=Emitovano je zvono u sesiji koja nije vidljiva.
Comment[sv]=Ljudsignal avgiven inne i en icke-synlig session
Comment[uk]=Звучить гудок у невидимому сеансі
Comment[x-test]=xxBell emitted within a non-visible sessionxx
Comment[zh_CN]=不可见会话中发生的响铃
Comment[zh_TW]=非可見工作階段中的響鈴行為
Sound=KDE-Sys-App-Message.ogg
Action=Popup


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>yakuake</Name>
        <Homepage>http://extragear.kde.org/apps/yakuake</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>GPLv2</License>
        <Icon>yakuake</Icon>
        <IsA>app:gui</IsA>
        <Summary>Very powerful Quake style Konsole for KDE4</Summary>
        <Description>The name comes from Yet Another Kuake (thus YaKuake). Its behaviour is similar to the console of the Quake game.</Description>
        <Archive sha1sum="4abe2401f7745861d44a11f47d036c9c535464a1" type="tarxz">mirrors://kde/stable/yakuake/3.0.4/src/yakuake-3.0.4.tar.xz</Archive>
        <BuildDependencies>
            <Dependency versionFrom="5.7.1">qt5-base-devel</Dependency>
            <Dependency>libX11-devel</Dependency>
            <Dependency versionFrom="5.7.1">qt5-x11extras-devel</Dependency>
            <Dependency>knewstuff-devel</Dependency>
            <Dependency>kio-devel</Dependency>
            <Dependency>kparts-devel</Dependency>
            <Dependency>knotifyconfig-devel</Dependency>
            <Dependency>karchive-devel</Dependency>
            <Dependency>kcoreaddons-devel</Dependency>
            <Dependency>kconfig-devel</Dependency>
            <Dependency>kcrash-devel</Dependency>
            <Dependency>kdbusaddons-devel</Dependency>
            <Dependency>kglobalaccel-devel</Dependency>
            <Dependency>ki18n-devel</Dependency>
            <Dependency>kiconthemes-devel</Dependency>
            <Dependency>knewstuff-devel</Dependency>
            <Dependency>knotifications-devel</Dependency>
            <Dependency>kparts-devel</Dependency>
            <Dependency>kwidgetsaddons-devel</Dependency>
            <Dependency>kwindowsystem-devel</Dependency>
            <Dependency>gettext-devel</Dependency>
            <Dependency>extra-cmake-modules</Dependency>
            <Dependency>kwayland-devel</Dependency>
        </BuildDependencies>
    </Source>

    <Package>
        <Name>yakuake</Name>
        <RuntimeDependencies>
            <Dependency versionFrom="5.7.1">qt5-base</Dependency>
            <Dependency versionFrom="5.7.1">qt5-x11extras</Dependency>
            <Dependency>kio</Dependency>
            <Dependency>kcrash</Dependency>
            <Dependency>ki18n</Dependency>
            <Dependency>kparts</Dependency>
            <Dependency>kconfig</Dependency>
            <Dependency>kxmlgui</Dependency>
            <Dependency>karchive</Dependency>
            <Dependency>kservice</Dependency>
            <Dependency>knewstuff</Dependency>
            <Dependency>kwayland</Dependency>
            <Dependency>kcoreaddons</Dependency>
            <Dependency>kdbusaddons</Dependency>
            <Dependency>kiconthemes</Dependency>
            <Dependency>kglobalaccel</Dependency>
            <Dependency>knotifyconfig</Dependency>
            <Dependency>kwindowsystem</Dependency>
            <Dependency>kconfigwidgets</Dependency>
            <Dependency>knotifications</Dependency>
            <Dependency>kwidgetsaddons</Dependency>
            <Dependency>libX11</Dependency>
            <Dependency>libgcc</Dependency>
        </RuntimeDependencies>
        <Files>
            <Path fileType="config">/etc/xdg</Path>
            <Path fileType="executable">/usr/bin</Path>
            <Path fileType="library">/usr/lib</Path>
            <Path fileType="library">/usr/lib/qt5</Path>
            <Path fileType="data">/usr/share</Path>
            <Path fileType="localedata">/usr/share/locale</Path>
            <Path fileType="doc">/usr/share/doc</Path>
        </Files>
        <AdditionalFiles>
            <AdditionalFile owner="root" permission="0644" target="/usr/share/kde4/apps/yakuake/yakuake.notifyrc">yakuake.notifyrc</AdditionalFile>
        </AdditionalFiles>
    </Package>

    <History>
        <Update release="4">
            <Date>2017-09-16</Date>
            <Version>3.0.4</Version>
            <Comment>Rebuild for new Toolchain.</Comment>
            <Name>Mustafa Cinasal</Name>
            <Email>muscnsl@gmail.com</Email>
        </Update>
        <Update release="3">
            <Date>2017-03-02</Date>
            <Version>3.0.2</Version>
            <Comment>Rebuild for new Toolchain.</Comment>
            <Name>Stefan Gronewold(groni)</Name>
            <Email>groni@pisilinux.org</Email>
        </Update>
        <Update release="2">
            <Date>2016-05-23</Date>
            <Version>3.0.2</Version>
            <Comment>Release bump</Comment>
            <Name>Stefan Gronewold(groni)</Name>
            <Email>groni@pisilinux.org</Email>
        </Update>
        <Update release="1">
            <Date>2016-03-07</Date>
            <Version>3.0.2</Version>
            <Comment>First release</Comment>
            <Name>Stefan Gronewold(groni)</Name>
            <Email>groni@pisilinux.org</Email>
        </Update>
    </History>
</PISI>

repo_name: ayhanyalcinsoy/main


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>yakuake</Name>
        <Summary xml:lang="tr">KDE5 için Quake tarzı oldukça güçlü bir Konsole</Summary>
        <Description xml:lang="tr">İsmi Bir Başka Kuake (Yet Another Kuake - YaKuake) sözcüklerinden gelmektedir. Quake oyunundaki konsola benzer.</Description>
    </Source>
</PISI>


code:
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 3.
# See the file http://www.gnu.org/licenses/gpl.txt

from pisi.actionsapi import autotools
from pisi.actionsapi import pisitools
from pisi.actionsapi import get

def build():
    pisitools.dosed("Makefile", "MANDIR = /usr/man", "MANDIR = /usr/share/man")
    autotools.make("-C po update-po")
    autotools.make()

def install():
    autotools.rawInstall("DESTDIR=%s" % get.installDIR())

    pisitools.remove("/usr/sbin/ntsysv")
    pisitools.remove("/sbin/chkconfig")
    pisitools.remove("/usr/share/man/man8/chkconfig.8")
    pisitools.remove("/usr/share/man/man8/ntsysv.8")
    pisitools.removeDir("/usr/share/man/man5")
    pisitools.removeDir("/sbin")

    pisitools.dodoc("COPYING")


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>alternatives</Name>
        <Homepage>http://git.fedorahosted.org/git/?p=chkconfig.git;a=summary</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>GPLv2</License>
        <IsA>app:console</IsA>
        <Summary>Alternative management system</Summary>
        <Description>alternatives creates, removes, maintains and displays information about the symbolic links comprising the alternatives system. Alternatives system determines the defaults when several programs fulfilling the same or similar functions to be installed on a single system at the same time.</Description>
        <Archive sha1sum="d2111640833792e96859559113c89467ea0f9a80" type="targz">https://github.com/fedora-sysv/chkconfig/archive/chkconfig-1.3.63.tar.gz</Archive>
        <BuildDependencies>
            <Dependency>popt-devel</Dependency>
            <Dependency>newt-devel</Dependency>
        </BuildDependencies>
    </Source>

    <Package>
        <Name>alternatives</Name>
        <Files>
            <Path fileType="config">/etc</Path>
            <Path fileType="executable">/usr/sbin</Path>
            <Path fileType="localedata">/usr/share/locale</Path>
            <Path fileType="man">/usr/share/man</Path>
            <Path fileType="doc">/usr/share/doc</Path>
            <Path fileType="data">/var</Path>
        </Files>
    </Package>

    <History>
        <Update release="3">
            <Date>2017-01-26</Date>
            <Version>1.3.63</Version>
            <Comment>Rebuild for new toolchain.</Comment>
            <Name>Alihan Öztürk</Name>
            <Email>alihan@pisilinux.org</Email>
        </Update>
        <Update release="2">
            <Date>2016-05-02</Date>
            <Version>1.3.63</Version>
            <Comment>Rebuild</Comment>
            <Name>Ayhan Yalçınsoy</Name>
            <Email>ayhanyalcinsoy@pisilinux.org</Email>
        </Update>
        <Update release="1">
            <Date>2016-03-05</Date>
            <Version>1.3.63</Version>
            <Comment>First release</Comment>
            <Name>Ayhan Yalçınsoy</Name>
            <Email>ayhanyalcinsoy@pisilinux.org</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>alternatives</Name>
        <Summary xml:lang="tr">Alternatif yönetim sistemi</Summary>
        <Description xml:lang="tr">alternatives, alternatives sisteminin parçası olan sembolik bağlantıları oluşturan, silen, yöneten ve bunlar hakkında bilgi gösteren araçtır. Alternatives sistemi, birbirine benzer ya da aynı işlevleri sunan programların aynı anda aynı sisteme kurulduğu zamanlarda öntanımlıları belirler.</Description>
    </Source>
</PISI>


code:
<PISI>
    <Name>x11.util</Name>
</PISI>


code:
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 3.
# See the file http://www.gnu.org/licenses/gpl.txt

from pisi.actionsapi import autotools
from pisi.actionsapi import pisitools
from pisi.actionsapi import get

def setup():
    autotools.configure("--with-mapdir=/usr/share/fonts/util")

def build():
    autotools.make()

def install():
    autotools.rawInstall("DESTDIR=%s" % get.installDIR())

    pisitools.dodoc("ChangeLog", "COPYING", "README")


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>font-util</Name>
        <Homepage>http://www.x.org/</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>MIT</License>
        <IsA>app:console</IsA>
        <Summary>X.Org font utilities</Summary>
        <Description>font-util package contains core font utilities for the Xorg XWindow system.</Description>
        <Archive sha1sum="0b16add3637c64b0bbaf1dd223b71b0421100c20" type="tarbz2">mirrors://xorg/individual/font/font-util-1.3.1.tar.bz2</Archive>
        <BuildDependencies>
            <Dependency>zlib-devel</Dependency>
        </BuildDependencies>
    </Source>

    <Package>
        <Name>font-util</Name>
        <Files>
            <Path fileType="executable">/usr/bin</Path>
            <Path fileType="data">/usr/lib/pkgconfig</Path>
            <Path fileType="data">/usr/share/fonts</Path>
            <Path fileType="data">/usr/share/aclocal</Path>
            <Path fileType="doc">/usr/share/doc</Path>
            <Path fileType="man">/usr/share/man</Path>
        </Files>
    </Package>

    <History>
        <Update release="3">
            <Date>2017-01-26</Date>
            <Version>1.3.1</Version>
            <Comment>Rebuild</Comment>
            <Name>Alihan Öztürk</Name>
            <Email>alihan@pisilinux.org</Email>
        </Update>
        <Update release="2">
            <Date>2016-05-02</Date>
            <Version>1.3.1</Version>
            <Comment>Rebuild</Comment>
            <Name>Burak Ertürk</Name>
            <Email>burakerturk@pisilinux.org</Email>
        </Update>
        <Update release="1">
            <Date>2015-05-10</Date>
            <Version>1.3.1</Version>
            <Comment>First release</Comment>
            <Name>Burak Ertürk</Name>
            <Email>burakerturk@pisilinux.org</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>font-util</Name>
        X.Org font araçları
        Utilitaires de fontes de X.Org
        X.Org Schrift Werkzeuge
    </Source>
</PISI>


code:
# -*- coding: utf-8 -*-
#
# Licensed under the GNU General Public License, version 3.
# See the file http://www.gnu.org/licenses/gpl.txt

from pisi.actionsapi import pisitools
from pisi.actionsapi import pythonmodules

def install():
    pythonmodules.install("--install-lib=/usr/lib/pardus")

    pisitools.dosym("zorg-cli", "/usr/bin/zorg")

    # This is now provided by xorg-server package.
    pisitools.remove("/usr/share/X11/DriversDB")


code:
# -*- coding: utf-8 -*-

from pisi.version import Version

def postInstall(fromVersion, fromRelease, toVersion, toRelease):
    if fromVersion and Version("0.90") <= Version(fromVersion) <= Version("0.96"):
        from zorg import config

        busId = call("zorg", "Xorg.Display", "activeDeviceID")
        device = config.getDeviceInfo(busId)

        if device:
            config.saveDeviceInfo(device)
            config.saveXorgConfig(device)

    if fromRelease and int(fromRelease) < 81:
        from zorg import config

        layout, variant = config.getKeymapOld()
        config.saveKeymap(layout, variant)


code:
# -*- coding: utf-8 -*-

import os

import comar
from pardus.sysutils import *

from zorg.config import *
from zorg.consts import *
from zorg.probe import *
from zorg.utils import *

kernel_module_file = os.path.join(config_dir, "kernel_module")

def getDevice():
    bus = configuredBus()
    if not bus:
        return None

    return getDeviceInfo(bus)

def getDeviceOutput(output):
    dev = getDevice()
    if not dev:
        return None, None

    if output not in dev.outputs:
        dev.outputs[output] = Output(output)

    return dev, dev.outputs[output]

# Model methods

def ready(boot):
    opts = {}

    if boot:
        opts = get_kernel_option("xorg")

    if "keymap" in opts:
        keymap = opts["keymap"].split("/", 1)
        setKeymap(*keymap)

    if "safe" in opts:
        return False

    driver = opts.get("driver")

    if driver or "probe" in opts:
        return initialConfig(driver)

    busId = configuredBus()
    if not busId:
        return initialConfig()

    if boot:
        # Check if the active card is changed
        try:
            device = getDeviceInfo(busId)
        except IOError:
            # Device is not present on specified bus.
            # Check if the new device is configured before.
            newBus = getPrimaryCard()
            if newBus:
                device = getDeviceInfo(VideoDevice(newBus).bus_id)
                if device and not device.isChanged():
                    # Yes, it is configured before.
                    # Reenable driver and write xorg.conf.
                    device.enableDriver()
                    saveXorgConfig(device)
                else:
                    # This is a different card.
                    return initialConfig()
            else:
                # No card is present. Return True in order to
                # start with a manually edited xorg.conf.
                return True
        else:
            if not device or device.isChanged():
                return initialConfig()

    # Check kernel module. Later, this should be done
    # with Xorg.Driver.ready method.
    if os.path.exists(kernel_module_file):
        kernelModule = file(kernel_module_file).read()
        if run("/sbin/modprobe", "-i", kernelModule) and kernelModule != "fglrx":
            return False

    return True

def initialConfig(preferredDriver=None):
    bus = getPrimaryCard()

    if bus:
        device = VideoDevice(bus)
    else:
        # This machine might be a terminal server with no video cards.
        # We start X and leave the decision to the user.
        log_debug("No video card found.")
        return True

    if preferredDriver is None:
        preferredDriver = device.preferredDriver()

    device.setDriver(preferredDriver)

    saveDeviceInfo(device)
    saveXorgConfig(device)

    return True

def safeConfig():
    bus = getPrimaryCard()

    if bus:
        device = VideoDevice(bus)
    else:
        # See the comment in initialConfig
        return True

    device.setDriver("vesa")
    device.depth = 16
    device.outputs["default"] = Output("default")
    device.monitors["default"] = Monitor()

    saveDeviceInfo(device)
    saveXorgConfig(device)

    return True

# FIXME Confusing method name
def activeDeviceID():
    return configuredBus()

def listDrivers():
    drivers = listAvailableDrivers()

    link = comar.Link()
    packages = list(link.Xorg.Driver)
    for package in packages:
        try:
            info = link.Xorg.Driver[package].getInfo()
        except dbus.DBusException:
            continue
        alias = str(info["alias"])
        driver = str(info["xorg-module"])
        drivers.append(alias)
        if driver in drivers:
            drivers.remove(driver)

    return sorted(drivers)

def setDriver(driver):
    dev = getDevice()
    if dev:
        dev.setDriver(driver)
        saveDeviceInfo(dev)

def setDepth(depth):
    dev = getDevice()
    if dev:
        dev.depth = depth
        saveDeviceInfo(dev)

def setOutput(output, enabled, ignored):
    dev, out = getDeviceOutput(output)

    if dev:
        out.setEnabled(enabled)
        out.setIgnored(ignored)

        saveDeviceInfo(dev)

def setMode(output, resolution, rate):
    dev, out = getDeviceOutput(output)

    if dev:
        out.setMode(resolution, rate)

        saveDeviceInfo(dev)

def setOrientation(output, rotation, reflection):
    dev, out = getDeviceOutput(output)

    if dev:
        out.setOrientation(rotation, reflection)

        saveDeviceInfo(dev)

def setPosition(output, position, arg):
    dev, out = getDeviceOutput(output)

    if dev:
        out.setPosition(position, arg)

        saveDeviceInfo(dev)

def setMonitor(output, vendor, model, horizSync, vertRefresh):
    dev, out = getDeviceOutput(output)

    if dev:
        if model:
            mon = Monitor()
            mon.vendor  = vendor
            mon.model   = model
            mon.hsync   = horizSync
            mon.vref    = vertRefresh
            dev.monitors[output] = mon
        elif output in dev.monitors:
            del dev.monitors[output]
        else:
            return

        saveDeviceInfo(dev)

def syncConfigs():
    dev = getDevice()
    if dev:
        saveXorgConfig(dev)

def setKeymap(layout, variant=""):
    saveKeymap(layout, variant)


code:
Index: zorg-2.0.4/zorg/config.py
===================================================================
--- zorg-2.0.4.orig/zorg/config.py
+++ zorg-2.0.4/zorg/config.py
@@ -11,6 +11,21 @@ from zorg.parser import *
 from zorg.probe import VideoDevice, Monitor, Output
 from zorg.utils import *

+KEYMAP_CONF_TEMPLATE="""\
+# This file is generated by zorg. If you want to change settings
+# in this file, edit the file 10-keyboard.conf in the same
+# directory.
+
+Section "InputClass"
+    Identifier      "Configured Keymap"
+    MatchIsKeyboard "on"
+    MatchTag        "use_configured_keymap"
+
+    %(comment_layout)sOption          "xkb_layout"    "%(layout)s"
+    %(comment_variant)sOption          "xkb_variant"   "%(variant)s"
+EndSection
+"""
+
 def saveXorgConfig(card):
     parser = XorgParser()

@@ -265,7 +280,7 @@ def saveDeviceInfo(card):
     f = open(consts.config_file, "w")
     f.write(doc.toPrettyString().replace("\n\n", ""))

-def getKeymap():
+def getKeymapOld():
     layout = None
     variant = ""

@@ -297,24 +312,44 @@ def getKeymap():

     return layout, variant

-def saveKeymap(layout, variant=""):
-    if not os.path.exists(consts.config_dir):
-        os.mkdir(consts.config_dir, 0755)
+def getKeymap():
+    layout = None
+    variant = ""

     try:
-        doc = piksemel.parse(consts.config_file)
+        p = XorgParser()
+        p.parseFile(consts.keymap_conf)
+        section = p.getSections("InputClass")[0]
+        layout = section.get("xkb_layout")
+        variant = section.get("xkb_variant", "")
+
     except OSError:
-        doc = piksemel.newDocument("ZORG")
+        pass
+
+    if not layout:
+        from pardus.localedata import languages

-    keyboardTag = doc.getTag("Keyboard")
+        try:
+            language = file("/etc/mudur/language").read().strip()
+        except IOError:
+            language = "en"
+
+        if not languages.has_key(language):
+            language = "en"

-    if keyboardTag:
-        keyboardTag.hide()
+        keymap = languages[language].keymaps[0]
+        layout = keymap.xkb_layout
+        variant = keymap.xkb_variant

-    keyboardTag = doc.insertTag("Keyboard")
-    keyboardTag.insertTag("Layout").insertData(layout)
-    if variant:
-        keyboardTag.insertTag("Variant").insertData(variant)
+    return layout, variant

-    f = file(consts.config_file, "w")
-    f.write(doc.toPrettyString().replace("\n\n", ""))
+def saveKeymap(layout, variant=""):
+    opts = {"layout": layout,
+            "variant": variant,
+            "comment_layout": "" if layout else "#",
+            "comment_variant": "" if variant else "#"}
+
+    with open(consts.keymap_conf, "w") as f:
+        f.write(KEYMAP_CONF_TEMPLATE % opts)
+        f.flush()
+        os.fsync(f.fileno())
Index: zorg-2.0.4/zorg/consts.py
===================================================================
--- zorg-2.0.4.orig/zorg/consts.py
+++ zorg-2.0.4/zorg/consts.py
@@ -8,6 +8,7 @@ data_dir    = "/usr/share/X11"
 modules_dir = "/usr/lib/xorg/modules"

 xorg_conf_file      = "/etc/X11/xorg.conf"
+keymap_conf         = "/etc/X11/xorg.conf.d/00-configured-keymap.conf"
 config_file         = join(config_dir,  "config.xml")
 configured_bus_file = join(config_dir,  "configured_bus")
 drivers_file        = join(data_dir,    "DriversDB")


code:
Index: zorg-cli
===================================================================
--- zorg-cli	(revision 31546)
+++ zorg-cli	(revision 31547)
@@ -94,8 +94,12 @@
         setDriver(opts.driver)

     elif opts.keymap:
-        keymap = opts.keymap.split("/", 1)
-        link.Xorg.Display["zorg"].setKeymap(*keymap)
+        if "/" in opts.keymap:
+            layout, variant = opts.keymap.split("/", 1)
+        else:
+            layout, variant = opts.keymap, ""

+        link.Xorg.Display["zorg"].setKeymap(layout, variant)
+
     else:
         parser.print_help()


code:
Section "Module"
    SubSection "extmod"
        Option  "omit xfree86-dga" "true"
    EndSubSection
EndSection

Section "ServerFlags"
    Option  "BlankTime" "0"
    Option  "OffTime" "0"
    Option  "SuspendTime" "0"
    Option  "AllowMouseOpenFail" "true"
    Option  "StandbyTime" "0"
EndSection

Section "Device"
    Identifier  "VideoCard"
    Driver  "vesa"
EndSection

Section "Screen"
    Identifier  "Screen"
    Device  "VideoCard"
    Monitor "Default Monitor"
    DefaultDepth    16
    SubSection "Display"
        Depth   16
        Modes "800x600" "640x480"
    EndSubSection
EndSection

Section "ServerLayout"
    Identifier  "Layout"
    Screen  "Screen"
EndSection

Section "Monitor"
    Identifier  "Default Monitor"
    HorizSync   31.5 - 50
    VertRefresh 50 - 70
EndSection


code:
<?xml version="1.0" ?>
<!DOCTYPE PISI SYSTEM "http://www.pisilinux.org/projeler/pisi/pisi-spec.dtd">
<PISI>
    <Source>
        <Name>zorg</Name>
        <Homepage>http://www.pisilinux.org</Homepage>
        <Packager>
            <Name>PisiLinux Community</Name>
            <Email>admins@pisilinux.org</Email>
        </Packager>
        <License>GPLv2</License>
        <IsA>app:console</IsA>
        <Summary>Çomar's X configuration tools</Summary>
        <Description>zorg is a set of X configuration tools and monitor and display adapter database files.</Description>
        <Archive sha1sum="e643f28430d9c6f190a5164e87960ae0094f0cd0" type="targz">https://github.com/forYali/zorg/archive/2.0.4.tar.gz</Archive>
        <Patches>
            <Patch level="1">keyboard-config.patch</Patch>
            <!--Patch>setkeymap-signature.patch</Patch-->
        </Patches>
    </Source>

    <Package>
        <Name>zorg</Name>
        <Files>
            <Path fileType="executable">/sbin</Path>
            <Path fileType="executable">/usr/bin</Path>
            <Path fileType="library">/usr/lib</Path>
            <Path fileType="data">/usr/share/X11</Path>
            <Path fileType="doc">/usr/share/doc</Path>
            <Path fileType="data">/var/lib/zorg</Path>
        </Files>
        <AdditionalFiles>
            <AdditionalFile owner="root" permission="0644" target="/usr/share/X11/xorg-safe.conf">xorg-safe.conf</AdditionalFile>
        </AdditionalFiles>
        <Provides>
            <COMAR script="xorg.display.py">Xorg.Display</COMAR>
            <COMAR script="package.py">System.Package</COMAR>
        </Provides>
    </Package>

    <History>
        <Update release="1">
            <Date>2017-03-05</Date>
            <Version>2.0.4</Version>
            <Comment>First release</Comment>
            <Name>Ertuğrul Erata</Name>
            <Email>ertugrulerata@gmail.com</Email>
        </Update>
    </History>
</PISI>


code:
<?xml version="1.0" ?>
<PISI>
    <Source>
        <Name>zorg</Name>
        Çomar'ın X yapılandırma araçları
        <Description xml:lang="tr">X yapılandırma araçları, monitör ve ekran kartı veritabanları</Description>
        <Description xml:lang="fr">outils de configuration de X, fichiers de base de données des écrans et des cartes vidéo.</Description>
        Çomar's X-Konfigurationtools
    </Source>
</PISI>


code:
<!DOCTYPE html>

    <meta>
    <title>Emlar - Blog</title>

    <meta charset="UTF-8">
    <link rel="stylesheet" href="/css/main.css"/>
    <meta name="viewport" content="width=device-width,initial-scale=1">

			photography

			contact

			blog

Emlar.xyz - Blog

Schools and Free Software

license: mit


code:
<DOCTYPE html>

    <title>Free software alternatives for schools - Emlar.xyz</title>

    <meta charset="UTF-8">
    <link rel="stylesheet" href="/css/main.css"/>
    <meta name="viewport" content="width=device-width,initial-scale=1">

			photography

			contact

			blog

<a href="/" class="Names"Emlar.xyz - Blog

Alternatives

A list of free and open source alternatives for common software used in schools.

I have included the software that is used in my school. If you think something should be added here contact me.

Windows - GNU/Linux. Most laptops come with Windows installed but installing something like Linux Mint is very easy.

Google classroom - No good alternatives. Schools in Estonia use Stuudium or E-Kool for their homework and grading, Stuudium (don't know about E-Kool) also has a built in work assigning, submitting and messaging system. While Stuudium is propietary it is still better than Google Classroom. I don't know about other countries.

Google Docs, Slides, Microsoft Office - Libre Office

Zoom - Jitsi Meet. The school would need to host a Jitsi instance or could use a public instance.

Gmail - Some schools have their students use school made emails - I dont see why that is necessary.

Facebook Messenger - Email or Matrix. Could host a private Matrix instance or use a public one.


code:
<DOCTYPE html>

    <title>Schools and free softwre - emlar.xyz</title>
    <meta name="description" content="Why do most schools use propietary software? What are the reasons to use free software?">

    <meta charset="UTF-8">
    <link rel="stylesheet" href="/css/main.css"/>
    <meta name="viewport" content="width=device-width,initial-scale=1">

			photography

			contact

			blog

<a href="/" class="Names"Emlar.xyz - Blog

 Schools and Free Software

28.02.22

			Most schools use propietary software for everything, there are some exeptions but those are usually accidental. I don't know about the situation in other countries so I will write about what I see in Estonia.

			Schools use Windows, Google Services (Classroom, Slides, Docs, etc.), Zoom and much more propietary software. This is especially important now that there is more and more school work online. It is sadly necessary to use those services for school because if you aren't on Google Classroom for example you will not have acces to homework and other things.
By forcing students to use propietary software at school they are locking the students into a propietary software ecosystem. Even if a student wanted to stop using propietary software they couldn't.

			Why is using propietary software so bad? Because the user doesn't have control over the software, instead the software is controlling and tracking the user. This is not the case with free and open source software (FOSS) because FOSS is made in a way that respects the user: the software doesn't track or control the user, the software can be forked and changed when necessary.

			Why do schools prefer propietary software? I assume that it's because they don't know about the flaws of propietary software and for them it's easier to use it because they are probably more familiar with it. Using free/libre software in schools requires some effort from the school so it is very easy for them to go the easy route and use Google, Windows, Zoom. Windows comes preinstalled, setting up Google Services for school is simple, using FOSS alternatives often requires you to host them on a server (Not strictly necessary as it is possible to use public instances).

			Cost? A lot of propietary software has some kind of a cost - usually a subscription or a license. Free (as in freedom) software is usually also free as in gratis but hosting the software on a server would require the school to pay for a VPS or for electricity and internet if hosting it on their own server. VPSs aren't usually very expensive so I don't think it would be a lot more expensive for schools to use free software. With the servers there would also come some other expenses. Overall the cost would probably be similiar.

			Should a school prefer convenience over the freedom and privacy of it's students?

			I have put together a list of free and open source alternatives for software commonly used in schools: List

This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License


code:
<!DOCTYPE html>

    <meta>
    <title>Emlar - Contact</title>

    <meta charset="UTF-8">
    <link rel="stylesheet" href="/css/main.css"/>
    <meta name="viewport" content="width=device-width,initial-scale=1">

			photography

			contact

			blog

<a href="/" class="Names"Emlar.xyz - Contact

Email - emilarro@protonmail.com

Github - Emilarr

Matrix - @emlar:matrix.org


code:
:root
{
/* VARIABLES: Background, Text, Link-Hover, Other links  */

/* Red and Green  */
	--bg-1: #6a070a;
	--txt-1: #11b10c;
	--txt-1-2: #000000;
	--link-h-1: #40c03c;
	--link-1: #65ce61;

/* Original D  */
	--bg-2: #2e3138;
	--txt-2: #cbb9ad;
	--txt-2-2: #cbb9ad;
	--link-h-2: #65728d;
	--link-2: #8aa4b5;
/* Original L  */
	--bg-2-l: #FFEEEE;
	--txt-2-l: #2e3138;
	--txt-2-l: #2e3138;
	--link-h-2-l: #818387;
	--link-2-l: #575a5f;

/* Greenish Black&White  */
	--bg-3: #112211;
	--txt-3: #EEFFEE;
	--txt-3-2: #EEFFEE;
	--link-h-3: #24600A;
	--link-3: #65ce61;

/* CHANGE these ONLY  */
	--bg: var(--bg-2);
	--txt: var(--txt-2);
	--txt-2: var(--txt-2-2);
	--link-h: var(--link-h-2);
	--link: var(--link-2);
}

body {
	font-family: 'Georgia', serif;
	font-weight: bold;
	background: var(--bg);
	color: var(--txt);

	max-width: 95vw;
	max-height: 95vh;
}

main {
	width: 95vw;
	height: 95vh;
	text-align: left;
}

.home-h1 {
	position: absolute;
	top: 30%;
	left: 50%;
	transform: translate(-50%, -50%);
	font-size: calc(1em + 4vw);
}

.home-f {
	color: var(--txt-2);
	bottom: 1vh;
	position:absolute;
	left: 5vw;
	right: 5vw;
	width:90vw;
	height: auto;
	font-size: calc(1em + 0.75vw);
}

.paths, .paths-s {
	font-size: calc(1em + 2vw);
	text-align: right;
}
/*
.paths-s{
	font-size: calc(1em + 1vw);
	text-align:right;
}
*/

a:link.Names, a:visited.Names, a:active.Names, a:link.home-link, a:visited.home-link, a:hover.home-link, a:active.home-link {
	text-decoration: none;
	color: inherit;
}

.Photos {
	display: block;
	width: 75vw;
	height: auto;
	border: 3px solid #000;
	margin: auto;
	margin-left: auto;
	margin-right: auto;
}

.licenses {
	display: block;
	max-width: 75vw;
	height: auto;
	text-align: center;
	margin: auto;
	margin-right: auto;
	margin-left: auto;
}

.Names {
	text-align: center;
	max-width: 50vw;
	margin: auto;
	font-size: calc(1em + 1.4vw);
}

.list {
	text-align: left;
	max-width: 50vw;
	margin: auto;
	font-size: calc(1em + 1vw);
}

article {
	color: var(--txt-2);
	max-width: 70vw;
	text-align: left;
	margin: auto;
	font-size: calc(1em + 0.75vw)
}

a:link, a:visited, a:active{
	color:var(--link)
}
a:hover, a:hover.home-link, a:hover.Names{
	color: var(--link-h);
}


code:
<DOCTYPE html>

    <title>emlar.xyz</title>

    <meta charset="UTF-8">
    <link rel="stylesheet" href="css/main.css"/>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="author" content="Emil">
    <meta name="description" content="I am a GNU/Linux and free software enthusiast and a beginner photographer and programmer.">

			photography

			contact

			blog

			rss

EMLAR.xyz

I am a GNU/Linux and free software enthusiast and a beginner photographer and programmer.

This site is for sharing my photos and some ideas or thoughts in my blog.


code:
<!DOCTYPE html>

    <meta>
    <title>Emlar - Photography</title>

    <meta charset="UTF-8">
    <link rel="stylesheet" href="/css/main.css"/>
    <meta name="viewport" content="width=device-width,initial-scale=1">

			photography

			contact

			blog

<a href="/" class="Names"Emlar.xyz - Photography

Photo Project "Lonely Places"


code:
<!DOCTYPE html>

    <meta>
    <title>Emlar - Photography</title>

    <meta charset="UTF-8">
    <link rel="stylesheet" href="/css/main.css"/>
    <meta name="viewport" content="width=device-width,initial-scale=1">

			photography

			contact

			blog

<a href="/" class="Names"Emlar.xyz - Photography

Lonely Places

9.02.22

		This is a photo project I originally did for school. The goal was to photograph lonely places in Tallinn on film. All of the photos were shot on 35mm film in 2020.

		 As a part of the project I learned to develop film and make darkroom prints. I took a lot more photos but here is a selection.

This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.


code: I have not added the photos here because too larg


code:
<?xml version="1.0" encoding="utf-8"?>

<rss version="2.0">

<channel>

<title> EMLAR.xyz </title>
<link> https://emlar.xyz/ </link>
<description> I am a GNU/Linux and free software enthusiast and a beginner photographer and programmer. This is a site for sharing my photograpy and my thoughts and ideas in my blog. </description>
<lastBuildDate> Wed, April 13 2022 </lastBuildDate>

<item>
<title> Schools and Free Software, 28.02.22</title>
<link>https://emlar.xyz/blog/schools-foss</link>
<guid>https://emlar.xyz/blog/schools-foss</guid>
<description>Most schools use propietary software for everything. Why do they do it? What should they do instead?</description>
<pubDate>February 28 2022</pubDate>
</item>

<item>
<title> Photo project "Lonely Places", 3.02.22</title>
<link>https://emlar.xyz/photography/lonely-places</link>
<guid> https://emlar.xyz/photography/lonely-places</guid>
<description> This is a photo project I originally did for school. The goal was to photograph lonely places in Tallinn on film. All of the photos were shot on 35mm film in 2020.
As a part of the project I learned to develop film and make darkroom prints. I took a lot more photos but here is a selection. </description>
<pubDate>February 3 2022</pubDate>
</item>

</channel>
</rss>


code:
<?xml version="1.0" encoding="utf-8"?>

<rss version="2.0">

<channel>

<item>
<title> Schools and Free Software, 28.02.22</title>
<link>https://emlar.xyz/blog/schools-foss</link>
<guid>https://emlar.xyz/blog/schools-foss</guid>
<description>Most schools use propietary software for everything. Why do they do it? What should they do instead?</description>
<pubDate>February 28 2022</pubDate>
</item>

</channel>
</rss>


code:
<DOCTYPE html>

    <title>RSS - emlar.xyz</title>
    <meta name="description" content="RSS">

    <meta charset="UTF-8">
    <link rel="stylesheet" href="/css/main.css"/>
    <meta name="viewport" content="width=device-width,initial-scale=1">

			photography

			contact

			blog

<a href="/" class="Names"Emlar.xyz - RSS

 RSS

			RSS is a web feed that allows you to keep up to date with content of websites. To do that you need to use a RSS reader, I personally use and reccommend CommaFeed. To use a RSS reader you need to copy the link of the feed to the reader.

RSS feeds

Everything - Everything on emlar.xyz

Blog - Only the blog

Photography - Only Photography


code:
<?xml version="1.0" encoding="utf-8"?>

<rss version="2.0">

<channel>

<item>
<title> Photo project "Lonely Places", 3.02.22</title>
<link>https://emlar.xyz/photography/lonely-places</link>
<guid> https://emlar.xyz/photography/lonely-places</guid>
<description> This is a photo project I originally did for school. The goal was to photograph lonely places in Tallinn on film. All of the photos were shot on 35mm film in 2020.
As a part of the project I learned to develop film and make darkroom prints. I took a lot more photos but here is a selection. </description>
<pubDate>February 3 2022</pubDate>
</item>

</channel>
</rss>


code:
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
	<url>
		<loc>https://emlar.xyz/photography/lonely-places/</loc>
		<lastmod>2022-02-09T00:00:00+00:00</lastmod>
	</url>
	<url>
		<loc>https://emlar.xyz/blog/schools-foss/</loc>
		<lastmod>2022-02-28T00:00:00+00:00</lastmod>
	</url>
	<url>
		<loc>https://emlar.xyz/blog/</loc>
	</url>
	<url>
		<loc>https://emlar.xyz/contact/</loc>
	</url>
	<url>
		<loc>https://emlar.xyz/photography/</loc>
	</url>
	<url>
		<loc>https://emlar.xyz/</loc>
	</url>
</urlset>


code:
# Gaptcha

A GNU spirited Libre Captcha

Dependencies : JQuery, JQuery-UI

Logic : There are 2 things which pose difficulties for bots. Number of actions available, only 4 here yet and the context of the question.

Demonstration at : [https://caissalover.github.io/gaptcha]

User Guide :

1. Clone this repo.
2. Ensure you have jquery.min.js, jquery-ui.min.css, jquery-ui.min.js and images in your static folder
3. Ensure style attributes don't clash with your own styles.
4. From gaptcha.html, copy head contents to your head and body to your body. (Don't trust us. Check that there's no backdoor in the code.)
5. Create a div with id="gaptcha" where your final form element will be.
6. Extract form value and check the answer at your backend.
7. Give a star to gaptcha if you found it useful!

Troubleshooting :
1. Ensure your static files are linked correctly and you have jquery before jquery-ui

Contributing :

1. You can contribute by adding more instances of the captcha which include:
    - A set of images (Should be similar to each other in properties and different for the user)
    - Answer
    - Question posed to the user (Should be a no-brainer)

Guide to framing the question:

The question must be something contextual like "Drag the actor of movie Titanic to the box". This makes things difficult 2-fold for a bot. First, finding the actor, recognizing the image("always keep colored images") and dragging the respective image to the box.

Guide to selecting images:

Image classification is almost a solved task. Due to this, one should prevent having only a single image with high similarity to the words in question.

Future Goals:
1. Having more questions and answers so that users can choose on random from backend.
2. Removing jquery dependency and making it in javascript. (Improbable as I hate javascript)

repo_name: sagaracharya/gaptcha


code:
<!doctype html>

        <link rel="stylesheet" href="jquery-ui.min.css">

GAPTCHA

 Drag the answer to the box given below.

Virat Kohli is a ____man.

                Drag
answer
here


code: Not done yet. Coming soon, maybe, if my hatred for javascript decreases!


code:
var insideBox = false;

dragElement(document.getElementById("1"));
dragElement(document.getElementById("2"));
dragElement(document.getElementById("3"));
dragElement(document.getElementById("4"));

function dragElement(elmnt) {
  // if insideBox return;                               Besta
  var pos1, pos2, pos3, pos4;
  elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
  elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
  if (document.getElementById(elmnt.id + "header")) {
    // if present, the header is where you move the DIV from:
    document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
  } else {
    // otherwise, move the DIV from anywhere inside the DIV:
    elmnt.onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    e = e || window.event;
    e.preventDefault();
    // get the mouse cursor position at startup:
    pos3 = e.clientX;
    pos4 = e.clientY;
    document.onmouseup = closeDragElement;
    // call a function whenever the cursor moves:
    document.onmousemove = elementDrag;
  }

  function elementDrag(e) {
    e = e || window.event;
    e.preventDefault();
    // calculate the new cursor position:
    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;
    // set the element's new position:
    elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
    elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";

    /* if pos1 and pos2 lie within range of limits of box, insideBox = true, freeze ids 1,2,3,4                   Besta man */
  }

  function closeDragElement() {
    // stop moving when mouse button is released:
    // write logic if object is in box, change captcha parameters
    document.onmouseup = null;
    document.onmousemove = null;
  }
}

// if insideBox {
//      document.getElementById("1").disabled = true;
//      document.getElementById("2").disabled = true;
//      document.getElementById("3").disabled = true;
//      document.getElementById("4").disabled = true;
// }


code:
**/__pycache__
db.sqlite3
/htmlcov
/media
.coverage

repo_name: jazzyeagle/ccradio

path: .gitignore

license: unknown


code:
"""
Django settings for ccradio project.

Generated by 'django-admin startproject' using Django 2.1.2.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '5u0uh%1t0b#=mo31njow6&vyif7x_shdstqz^*xcoyz#9@26hs'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users.apps.UsersConfig',
    'website.apps.WebsiteConfig',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'ccradio.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'ccradio.wsgi.application'

# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# Password validation
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

AUTH_USER_MODEL = 'users.CCUser'
LOGIN_URL = '/users/login/'
LOGIN_REDIRECT_URL = 'index'
LOGOUT_URL = '/users/logout/'
LOGOUT_REDIRECT_URL = 'index'

# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'


code:
"""ccradio URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.views.generic.base import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', include('users.urls')),
    path('users/', include('django.contrib.auth.urls')),
    path('', include('website.urls')),
]


code:
"""
WSGI config for ccradio project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ccradio.settings')

application = get_wsgi_application()


code:
#!/usr/bin/env python
import os
import sys

if __name__ == '__main__':
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ccradio.settings')
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


code:
<!DOCTYPE html5>
<html>
    <head>
        <meta charset="utf-8">
        <title>{% block title %}CCRadio{% endblock %}</title>
    </head>
    <body>
        <main>
            {% block content %}
            {% endblock %}
        </main>
    </body>
</html>


code:
{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block content %}
    {% if user.is_authenticated %}

Welcome, {{ user.username }}!

        Logout
    {% else %}

You are not logged in

        Login
        Sign Up
    {% endif %}
{% endblock %}


code:
{% extends 'base.html' %}

{% block title %}Login{% endblock %}

{% block content %}

Login

    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Submit">
    </form>
{% endblock %}


code:
{% extends 'base.html' %}

{% block title %}Sign Up{% endblock %}

{% block content %}

Sign Up

    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Sign up</button>
    </form>
{% endblock %}


code:
{% extends 'website/base.html' %}

{% block title %}CCRadio - Album: {{ album.artist.name }} - {{ album.title }}{% endblock %}

{% block page %}

{{ album.artist.name }}

{{ album.title }}

        {% if member %}

                Add Song

        {% endif %}
        {% if songs %}

                {% for song in songs %}

{{song.title }}

                {% endfor %}

        {% else %}

No songs uploaded for this album yet.

        {% endif %}

{% endblock %}


code:
{% extends 'website/base.html' %}

{% block title %}CCRadio - Artist Form{% endblock %}

{% block page %}

        <form method="post">
            {% csrf_token %}
            {{ form.as_p }}

            <button type="submit">Create Album</button>
        </form>

{% endblock %}


code:
{% extends 'website/base.html' %}
{% load static %}

{% block title %}CCRadio - Artist: {{ artist.name }}{% endblock %}

{% block page %}

{{ artist.name }}
            {% if member %}

            {% endif %}

        {% if artist.bio %}

{{ artist.bio }}

        {% endif %}

Albums

        {% if albums %}

                {% for album in albums %}

{{ album.title }}

                {% endfor %}

        {% else %}

No albums for this artist yet!

        {% endif %}

{% endblock %}


code:
{% extends 'website/base.html' %}

{% block title %}CCRadio - Artist Form{% endblock %}

{% block page %}

Warning!  You are about to delete your artist page!

All information related to your artist page, including albums, songs, ratings, etc., will be permanently deleted.  It will be as if you never signed up the artist at all.

Are you sure you wish to proceed?

        <form method="post">
            {% csrf_token %}
            <button type="submit">Delete Artist</button>
            <button type="button">Cancel Deletion Request</button>
        </form>

{% endblock %}


code:
{% extends 'website/base.html' %}

{% block title %}CCRadio - Artist Form{% endblock %}

{% block page %}

        <form method="post">
            {% csrf_token %}
            {{ form.as_p }}

We know everyone hates reading End User License Agreements, so we'll keep ours as brief and as easy to read as possible.  PLEASE read this before creating an artist page...

This site/radio station is intended for music that is released under a Creative Commons license.  If you don't know what that means, I strongly recommend you click here to learn more about it before continuing.  In a brief nutshell, while you retain the copyright, you grant certain permissions to the public, depending on the level of licensure (e.g. sharing with friends openly, making covers of your songs, etc).  You can only upload music by selecting a type of Creative Commons licensure you're releasing under.  If you do not wish to release your music under a Creative Commons license, then this site is NOT for you.

Whenever you upload music to this website, you are attesting that you have the legal right to upload the music.  This means either you retain the copyright to the song (most typically if you wrote the song yourself) OR you have permission from the copyright holder to upload the song (e.g. a cover of a song that was released under a CC-BY-SA license).  If we learn that you do not have permission to upload a song to our website, the song will be deleted.  Repeated offenses will lead in disabling your Artist page and potentially your user account.

If you are releasing a song under a Creative Commons license with a Non-Commerical stipulation, here is important information you need to know:  We do accept donations from those who wish to give them for any reason.  That being said, some people and/or legal entities may have the opinion that this website/radio station is broadcasting your song Commerically.  By uploading songs to this website, you are granting express consent for us to use your music on the website and on the radio station royalty-free, even if it is deemed that we are using it Commercially.

If you agree to all that, click here...

            <button type="submit">Create Artist</button>
        </form>

{% endblock %}


code:
{% extends 'website/base.html' %}
{% load static %}

{% block title %}CCRadio - Artist Form{% endblock %}

{% block page %}

        <form method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">Update Artist</button>
        </form>

Delete Artist

{% endblock %}


code:
{% extends 'website/base.html' %}

{% block title %}CCRadio - Artists{% endblock %}

{% block page %}

        {% if artists %}

                {% for artist in artists %}

{{ artist.handle }} - {{ artist.name }}

                {% endfor %}

        {% else %}

No artists have signed up.

        {% endif %}

{% endblock %}


code:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>
            {% block title %}CCRadio{% endblock %}
        </title>
        {% block header %}
        {% endblock %}
    </head>
    <body>
        {% block page %}
        {% endblock %}
    </body>
</html>


code:
{% extends 'website/base.html' %}
{% load static %}
{% block title %}CCRadio{% endblock %}

{% block header %}
    <link rel="stylesheet" href={% static 'website/index.css' %}>
{% endblock %}

{% block page %}

{% endblock %}


code:
{% extends 'website/base.html' %}

{% load static %}
{% block header %}
    <link rel="stylesheet" href={% static 'website/navbar.css' %}>
    <link rel="stylesheet" href={% static 'website/grid.css' %}>
{% endblock %}

{% block page %}

                Player

                Search

                {% if user.is_authenticated %}
                    {{ user.username }} |
                    Logout
                {% else %}
                    Login / Register
                {% endif %}

{% endblock %}


code:
{% extends 'website/base.html' %}

{% block title %}CCRadio - Album: {{ album.artist.name }} - {{ album.title }}{% endblock %}

{% block page %}

        <form method="post" enctype="multipart/form-data">
            {% csrf_token %}
            {{ form.as_p }}

            <button type="submit">Add Song</button>
        </form>

{% endblock %}


code:
{% extends 'website/base.html' %}

{% block page %}

Welcome to CC Radio!

    Click here to go to artist search

    Create new artist
{% endblock %}


code:
from django.test import TestCase
from model_mommy import mommy

from users.models import CCUser
from website.models import Artist, Album, Song, artist_path, album_path, song_path

class UserModelTests(TestCase):
    def test_user_creation(self):
        u = mommy.make(CCUser)
        self.assertTrue(isinstance(u, CCUser))
        self.assertEqual(u.__str__(), u.email)

class WebsiteModelTests(TestCase):
    def setUp(self):
        self.artist = mommy.make(Artist)
        self.album = mommy.make(Album)
        self.song = mommy.make(Song)
        self.filename = 'filename.ogg'

    def tearDown(self):
        del self.artist
        del self.album
        del self.song

    def test_artist_creation(self):
        self.assertTrue(isinstance(self.artist, Artist))
        self.assertEqual(self.artist.__str__(), self.artist.name)

    def test_artist_path(self):
        self.assertEqual(artist_path(self, self.filename), 'music/{0}/{1}'.format(self.artist.handle, self.filename))

    def test_album_creation(self):
        self.assertTrue(isinstance(self.album, Album))
        self.assertEqual(self.album.__str__(), self.album.artist.name + ' - ' + self.album.title)

    def test_album_path(self):
        self.assertEqual(album_path(self, self.filename), 'music/{0}/{1}/{2}'.format(self.album.artist.handle, self.album.title, self.filename))

    def test_song_creation(self):
        self.assertTrue(isinstance(self.song, Song))
        self.assertEqual(self.song.__str__(), self.song.artist.name + ' - ' + self.song.title + ' (' + self.song.album.title + ')')

    def test_song_path(self):
        self.assertEqual(song_path(self, self.filename), 'music/{0}/{1}/{2}'.format(self.song.artist.handle, self.song.album.title, self.filename))


code:
from django.test import LiveServerTestCase
from model_mommy import mommy
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from users.models import CCUser
from website.models import Artist, Album, Song, artist_path, album_path, song_path

class ViewTests(LiveServerTestCase):
    def setUp(self):
        #self.selenium = webdriver.Remote('localhost:8000', webdriver.DesiredCapabilities.HTMLUNIT.copy())
        #self.selenium = webdriver.Firefox()
        self.selenium = webdriver.Chrome()
        self.selenium.maximize_window()
        super(ViewTests, self).setUp()
        self.testuser = mommy.prepare(CCUser, username='somedude')
        self.test_password = '1l1k3ccr4d10'
        self.user = CCUser.objects.create_user(username=self.testuser.username, email='myfake@email.com', password=self.test_password)
        self.admin = CCUser.objects.create_superuser(username='admin', email='admin@admin.com', password='admin')
        self.artist1 = mommy.make(Artist)
        self.album11 = mommy.make(Album, artist=self.artist1)
        self.song111 = mommy.make(Song, artist=self.artist1, album=self.album11)
        self.song112 = mommy.make(Song, artist=self.artist1, album=self.album11)
        self.album12 = mommy.make(Album, artist=self.artist1)
        self.song121 = mommy.make(Song, artist=self.artist1, album=self.album12)
        self.song122 = mommy.make(Song, artist=self.artist1, album=self.album12)
        self.artist2 = mommy.make(Artist)
        self.album21 = mommy.make(Album, artist=self.artist2)
        self.song211 = mommy.make(Song, artist=self.artist2, album=self.album21)
        self.song212 = mommy.make(Song, artist=self.artist2, album=self.album21)
        self.album22 = mommy.make(Album, artist=self.artist2)
        self.song221 = mommy.make(Song, artist=self.artist2, album=self.album22)
        self.song222 = mommy.make(Song, artist=self.artist2, album=self.album22)

    def tearDown(self):
        self.selenium.quit()
        super(ViewTests, self).tearDown()

    def test_create_user(self):
        selenium = self.selenium
        selenium.get(self.live_server_url + '/users/signup/')
        createduser = mommy.prepare(CCUser)

        try:
            #WebDriverWait(selenium, 10).until(EC.title_contains('Sign Up'))

            username = selenium.find_element_by_id('id_username')
            email = selenium.find_element_by_id('id_email')
            password1 = selenium.find_element_by_id('id_password1')
            password2 = selenium.find_element_by_id('id_password2')

            username.send_keys(createduser.username)
            email.send_keys(createduser.email)
            password1.send_keys(self.test_password)
            password2.send_keys(self.test_password)
            username.submit()

            #WebDriverWait(selenium, 10).until(EC.title_contains('Login'))
            self.assertTrue('Login' in selenium.title)

        finally:
            selenium.quit()

    def test_login(self):
        selenium = self.selenium
        selenium.get(self.live_server_url + '/users/login/')

        try:
            #WebDriverWait(selenium, 10).until(EC.login_contains('Login'))

            username = selenium.find_element_by_id('id_username')
            password = selenium.find_element_by_id('id_password')
            username.send_keys(self.testuser.username)
            password.send_keys(self.test_password)
            username.submit()
            WebDriverWait(selenium, 360).until(EC.title_contains('CCRadio'))

            # Assertion: User is taken back to welcome page
            self.assertTrue('CCRadio' in selenium.title)
            # Assertion: User's username is listed in navbar
            selenium.switch_to.frame('navbar')
            usermenu = selenium.find_element_by_id('user-menu')
            self.assertTrue(self.testuser.username in usermenu.text)
            # Assertion: Logout link is shown in navbar
            logout_link = usermenu.find_element_by_link_text('Logout')
            self.assertTrue(logout_link.is_displayed())

        finally:
            selenium.quit()

    def test_artists_page(self):
        self.artists_page(False)
        self.artists_page(True)

    def artists_page(self, logged_in):
        if(logged_in):
            self.client.login(username=self.testuser.username, password=self.test_password)
        selenium = self.selenium
        selenium.get(self.live_server_url + '/artists')
        WebDriverWait(selenium, 360).until(EC.title_contains('Artists'))
        pagebody = selenium.find_element_by_id('artists_body')

        # There should be two artists.  Check that both are listed.
        self.assertTrue(self.artist1.name in pagebody.text)
        self.assertTrue(self.artist2.name in pagebody.text)

    def test_artist_page(self):
        self.artist_page(False)
        self.artist_page(True)

    def artist_page(self, logged_in):
        if(logged_in):
            self.client.login(username=self.testuser.username, password=self.test_password)
        for artist in Artist.objects.all():
            self.validate_artist_page(artist)

    def validate_artist_page(self, artist):
        selenium = self.selenium
        selenium.get(self.live_server_url + '/' + artist.handle)
        WebDriverWait(selenium, 360).until(EC.title_contains('Artist:'))
        pagebody = selenium.find_element_by_id('artist_body')
        for album in Album.objects.filter(artist=artist.handle):
            self.assertTrue(album.title in pagebody.text)

    def test_album_page(self):
        self.album_page(False)
        self.album_page(True)

    def album_page(self, logged_in):
        if(logged_in):
            self.client.login(username=self.testuser.username, password=self.test_password)
        for album in Album.objects.all():
            self.validate_album_page(album)

    def validate_album_page(self, album):
        selenium = self.selenium
        selenium.get(self.live_server_url + '/' + album.artist.handle + '/' + album.slug)
        WebDriverWait(selenium, 360).until(EC.title_contains('Album:'))
        pagebody = selenium.find_element_by_id('album_body')
        for song in Song.objects.filter(artist=album.artist, album=album):
            self.assertTrue(song.title in pagebody.text)


code:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .forms import CCUserCreationForm, CCUserChangeForm
from .models import CCUser

class CCUserAdmin(UserAdmin):
    add_form = CCUserCreationForm
    form = CCUserChangeForm
    model = CCUser
    list_display = ['email', 'username',]

admin.site.register(CCUser, CCUserAdmin)


code:
from django.apps import AppConfig

class UsersConfig(AppConfig):
    name = 'users'


code:
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CCUser

class CCUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm):
        model = CCUser
        fields = ('username', 'email')

class CCUserChangeForm(UserChangeForm):
    class Meta:
        model = CCUser
        fields = ('username', 'email')


code:
# Generated by Django 2.1.2 on 2019-02-18 01:45

import django.contrib.auth.models
import django.contrib.auth.validators
from django.db import migrations, models
import django.utils.timezone

class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('auth', '0009_alter_user_last_name_max_length'),
    ]

    operations = [
        migrations.CreateModel(
            name='CCUser',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('password', models.CharField(max_length=128, verbose_name='password')),
                ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
                ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
                ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
                ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
                ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
                ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
                ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
                ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
                ('email', models.EmailField(max_length=254, unique=True)),
                ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
                ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
            ],
            options={
                'verbose_name': 'user',
                'verbose_name_plural': 'users',
                'abstract': False,
            },
            managers=[
                ('objects', django.contrib.auth.models.UserManager()),
            ],
        ),
    ]


code:
from django.contrib.auth.models import AbstractUser
from django.db import models

class CCUser(AbstractUser):
    email = models.EmailField(unique=True)

    def __str__(self):
        return self.email


code:
from django.urls import path
from . import views

urlpatterns = [
    path('signup/', views.SignUp.as_view(), name='signup'),
]


code:
from django.urls import reverse_lazy
from django.views import generic

from .forms import CCUserCreationForm

class SignUp(generic.CreateView):
    form_class = CCUserCreationForm
    success_url = reverse_lazy('login')
    template_name = 'signup.html'


code:
from django.contrib import admin
from .models import Artist, Album, Song, BandMember, Role, License

# Register your models her
admin.site.register(Artist)
admin.site.register(Album)
admin.site.register(Song)
admin.site.register(BandMember)
admin.site.register(Role)
admin.site.register(License)


code:
from django.apps import AppConfig

class WebsiteConfig(AppConfig):
    name = 'website'


code:
# Generated by Django 2.1.2 on 2019-02-18 02:20

import datetime
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc
import website.models

class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Album',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=200)),
                ('slug', models.SlugField()),
                ('cover', models.ImageField(null=True, upload_to=website.models.album_path)),
                ('license', models.CharField(max_length=25)),
                ('release_date', models.DateField(default=datetime.datetime(2019, 2, 18, 2, 20, 34, 846978, tzinfo=utc))),
                ('created_date', models.DateField(auto_now_add=True)),
                ('modified_date', models.DateField(auto_now=True)),
            ],
        ),
        migrations.CreateModel(
            name='Artist',
            fields=[
                ('handle', models.SlugField(primary_key=True, serialize=False)),
                ('name', models.CharField(max_length=200)),
                ('bio', models.CharField(blank=True, max_length=1000)),
                ('logo', models.ImageField(null=True, upload_to=website.models.artist_path)),
                ('created_date', models.DateField(auto_now_add=True)),
                ('modified_date', models.DateField(auto_now=True)),
            ],
        ),
        migrations.CreateModel(
            name='Song',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('title', models.CharField(max_length=200)),
                ('license', models.CharField(max_length=25)),
                ('nsfw', models.BooleanField(default=False)),
                ('file', models.FileField(upload_to=website.models.song_path)),
                ('release_date', models.DateField(default=datetime.datetime(2019, 2, 18, 2, 20, 34, 847904, tzinfo=utc))),
                ('created_date', models.DateField(auto_now_add=True)),
                ('modified_date', models.DateField(auto_now_add=True)),
                ('album', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Album')),
                ('artist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Artist')),
            ],
        ),
        migrations.AddField(
            model_name='album',
            name='artist',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Artist'),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-03-28 14:01

import datetime
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('website', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='BandMember',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('artist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Artist')),
                ('member', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
            ],
        ),
        migrations.CreateModel(
            name='Role',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('role', models.CharField(max_length=30)),
            ],
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 3, 28, 14, 1, 23, 897282, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 3, 28, 14, 1, 23, 898138, tzinfo=utc)),
        ),
        migrations.AddField(
            model_name='bandmember',
            name='role',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='website.Role'),
        ),
        migrations.AddField(
            model_name='artist',
            name='members',
            field=models.ManyToManyField(through='website.BandMember', to=settings.AUTH_USER_MODEL),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-03-29 16:32

import datetime
from django.db import migrations, models
from django.utils.timezone import utc
import website.models

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0002_auto_20190328_1401'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 3, 29, 16, 32, 10, 246259, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='artist',
            name='bio',
            field=models.CharField(blank=True, max_length=1000, null=True),
        ),
        migrations.AlterField(
            model_name='artist',
            name='logo',
            field=models.ImageField(blank=True, null=True, upload_to=website.models.artist_path),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 3, 29, 16, 32, 10, 247194, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-03-29 16:45

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0003_auto_20190329_1632'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='bandmember',
            name='role',
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 3, 29, 16, 45, 50, 850849, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 3, 29, 16, 45, 50, 851686, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-01 18:31

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0004_auto_20190329_1645'),
    ]

    operations = [
        migrations.CreateModel(
            name='License',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('abbreviation', models.CharField(max_length=20)),
                ('name', models.CharField(max_length=100)),
                ('image', models.URLField()),
                ('legaleze', models.URLField()),
            ],
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 18, 31, 29, 198022, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 18, 31, 29, 199392, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-01 18:47

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0005_auto_20190401_1831'),
    ]

    operations = [
        migrations.AddField(
            model_name='license',
            name='version',
            field=models.DecimalField(decimal_places=1, default=0.0, max_digits=3),
            preserve_default=False,
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 18, 47, 41, 723685, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 18, 47, 41, 724572, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-01 18:51

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0006_auto_20190401_1847'),
    ]

    operations = [
        migrations.RenameField(
            model_name='license',
            old_name='image',
            new_name='large_image',
        ),
        migrations.AddField(
            model_name='license',
            name='small_image',
            field=models.URLField(default='default'),
            preserve_default=False,
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 18, 50, 44, 621510, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 18, 50, 44, 622356, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-01 19:10

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0007_auto_20190401_1851'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='license',
            name='id',
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 19, 10, 37, 240429, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='license',
            name='abbreviation',
            field=models.CharField(max_length=20, primary_key=True, serialize=False),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 19, 10, 37, 241242, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-01 19:40

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0008_auto_20190401_1910'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='role',
            name='id',
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 19, 40, 9, 964515, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='role',
            name='role',
            field=models.CharField(max_length=30, primary_key=True, serialize=False),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 1, 19, 40, 9, 965414, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-02 12:49

import datetime
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0009_auto_20190401_1940'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='license',
            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='website.License'),
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 2, 12, 49, 1, 355187, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 2, 12, 49, 1, 356024, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-02 12:53

import datetime
from django.db import migrations, models
from django.utils.timezone import utc
import website.models

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0010_auto_20190402_1249'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='cover',
            field=models.ImageField(blank=True, null=True, upload_to=website.models.album_path),
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 2, 12, 53, 6, 525372, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 2, 12, 53, 6, 526202, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-08 23:27

import datetime
from django.db import migrations, models
import django.db.models.deletion
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0011_auto_20190402_1253'),
    ]

    operations = [
        migrations.AddField(
            model_name='song',
            name='slug',
            field=models.SlugField(default='byte-me', max_length=200),
            preserve_default=False,
        ),
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 27, 8, 744289, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='album',
            name='slug',
            field=models.SlugField(max_length=200),
        ),
        migrations.AlterField(
            model_name='song',
            name='license',
            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='website.License'),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 27, 8, 745167, tzinfo=utc)),
        ),
    ]


code:
# Generated by Django 2.1.7 on 2019-04-08 23:30

import datetime
from django.db import migrations, models
from django.utils.timezone import utc

class Migration(migrations.Migration):

    dependencies = [
        ('website', '0012_auto_20190408_2327'),
    ]

    operations = [
        migrations.AlterField(
            model_name='album',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 30, 28, 944842, tzinfo=utc)),
        ),
        migrations.AlterField(
            model_name='song',
            name='nsfw',
            field=models.BooleanField(blank=True, default=False, null=True),
        ),
        migrations.AlterField(
            model_name='song',
            name='release_date',
            field=models.DateField(default=datetime.datetime(2019, 4, 8, 23, 30, 28, 945918, tzinfo=utc)),
        ),
    ]