add compte rendu export and fields

This commit is contained in:
Tykayn 2024-01-20 22:47:33 +01:00 committed by tykayn
parent ff4bd2a868
commit 3d22b1f50f
3 changed files with 170 additions and 54 deletions

View File

@ -19,7 +19,8 @@
<strong>{{ i + 1 }}) {{ s.title }}</strong> <strong>{{ i + 1 }}) {{ s.title }}</strong>
<p class="is-small"> <p class="is-small">
{{ s.duration }} min, par {{ s.author }}. Reste: {{ resteTopicMinutes(s) }}. Passé: {{ round(s.spentSeconds) }}. {{ s.duration }} min, par {{ s.author }}. Reste: {{ resteTopicMinutes(s) }}.
Passé: {{ round(s.spentSeconds) }}.
</p> </p>
<p *ngIf="s.finished">🎉</p> <p *ngIf="s.finished">🎉</p>
</li> </li>
@ -34,17 +35,22 @@
<h2> <h2>
Texte du pad Texte du pad
</h2> </h2>
<textarea class="textarea mx-2 is-full" [(ngModel)]="pasteLand" id="pasteland" <textarea name="note" id="pasteland" cols="30" rows="10" class="textarea mx-2 is-full" [(ngModel)]="pasteLand"
id="pasteland"
(ngModelChange)="parseTheListOfTopicsInPasteLand()"></textarea> (ngModelChange)="parseTheListOfTopicsInPasteLand()"></textarea>
<h2> <h2>
Compte rendu Compte rendu du {{ (today) | date:'yyyy-MM-dd'}}
</h2> </h2>
<p> <pre>
{{buildCompteRendu()}} {{buildCompteRendu()}}
</p> </pre>
<button class="btn is-primary" (click)="copyCompteRenduToClipboard()"> <button class="btn is-primary" (click)="copyCompteRenduToClipboard()">
copier copier
</button> </button>
<button class="btn is-primary" (click)="downloadCompteRendu()">
télécharger le compte rendu
</button>
</div> </div>
<div class="liens"> <div class="liens">
@ -66,11 +72,11 @@
</div> </div>
<div class="explications"> <div class="explications">
<p> <p>
{{statsExplication}} {{statsExplication}}
</p> </p>
</div> </div>
<div class="debug" *ngIf="showDebug"> <div class="debug" *ngIf="showDebug">
debug: debug:
<pre> <pre>
@ -96,21 +102,38 @@
<p>Reste: {{ countRemainingMinutes(subjects[currentSubjectId]) }} min</p> <p>Reste: {{ countRemainingMinutes(subjects[currentSubjectId]) }} min</p>
<p>avancement: {{ getPercentProgressTimeForTopic(subjects[currentSubjectId]) }} %</p> <p>avancement: {{ getPercentProgressTimeForTopic(subjects[currentSubjectId]) }} %</p>
<p>Début: {{ startTime }}</p> <hr>
<p>Fin: {{ endTime }}</p> <div class="compte-rendu-infos">
<label for="scribe">scribe</label>
<input type="text" id="scribe" [(ngModel)]="scribe">
<br>
<label for="presents">{{countLinesInPresent()}}présents</label>
<textarea name="presents" id="note" cols="30" rows="10" id="presents" [(ngModel)]="presents"></textarea>
<br>
<label for="début">Début</label>
<input name="presents" id="début" [(ngModel)]="startTime"/>
<br>
<label for="fin">Fin</label>
<input name="presents" id="fin" [(ngModel)]="endTime"/>
<button class="btn btn-primary" (click)="previousSubject()"> </div>
précédent <div class="actions">
</button>
<button class="btn is-primary" (click)="nextSubject()"> <p>Début: {{ startTime }}, Fin: {{ endTime }}. Durée: {{round(getMinutesBetweenTwoDates(startDate,endDate))}}</p>
suivant
</button> <button class="btn btn-primary" (click)="previousSubject()">
<button class="btn is-success" (click)="finishTopic(currentSubjectId)"> précédent
fini </button>
</button> <button class="btn is-primary" (click)="nextSubject()">
<button class="btn btn-primary" (click)="updateProgressEveryPeriod()"> suivant
up temps </button>
</button> <button class="btn is-success" (click)="finishTopic(currentSubjectId)">
fini
</button>
<button class="btn btn-primary" (click)="updateProgressEveryPeriod()">
up temps
</button>
</div>
</div> </div>
</div> </div>

View File

@ -164,17 +164,35 @@ main {
border-left-color: #00b89c; border-left-color: #00b89c;
} }
} }
label{
margin-right: 2ch;
margin-top: 1rem;
min-width: 10rem;
display: inline-block;
}
.btn{ .btn{
cursor: pointer; cursor: pointer;
padding: 0.75rem 1.5rem; padding: 0.75rem 1.5rem;
border: solid 1px slategray; border: solid 1px slategray;
border-radius: 0.5rem; border-radius: 0.5rem;
float:left;
margin-right: 2ch;
display: inline-block;
} }
pre{ pre{
padding: 0.5rem; padding: 0.5rem;
background: lightslategrey; background: lightslategrey;
} }
.is-clickable{
cursor: pointer;
&:hover{
&.subject{
background: rgba(170, 210, 199,0.2);
}
}
}
@media screen and (max-width: 650px) { @media screen and (max-width: 650px) {
.content { .content {
flex-direction: column; flex-direction: column;

View File

@ -30,6 +30,11 @@ export class AppComponent implements OnInit, OnDestroy {
startTime: string = "20:30"; startTime: string = "20:30";
// startTime: string = "17:00"; // startTime: string = "17:00";
endTime: string = "22:00"; endTime: string = "22:00";
today: any = new Date();
// champs habituels pour le compte rendu:
scribe: string = 'tykayn';
presents: string = '- tykayn';
pasteLand: string = "* Présentation du suivi sur Nextcloud - 5min (tykayn)\n" + pasteLand: string = "* Présentation du suivi sur Nextcloud - 5min (tykayn)\n" +
"* Réduction de bus factor - 5min (tykayn)\n" + "* Réduction de bus factor - 5min (tykayn)\n" +
"* Réunion avec Wikimedia France - 5min (tykayn)\n" + "* Réunion avec Wikimedia France - 5min (tykayn)\n" +
@ -40,9 +45,11 @@ export class AppComponent implements OnInit, OnDestroy {
hints: string = ""; hints: string = "";
showDebug: boolean = true; showDebug: boolean = true;
private startDate: Date = new Date(); startDate: Date = new Date();
private endDate: Date = new Date(); endDate: Date = new Date();
private topicChangeDate: Date = new Date(); private topicChangeDate: Date = new Date();
private timekeeper: any;
private animator: any;
updateTopicChangeDate(): void { updateTopicChangeDate(): void {
@ -54,9 +61,10 @@ export class AppComponent implements OnInit, OnDestroy {
this.subjects[this.currentSubjectId].spentSeconds += timeDifferenceInSeconds; this.subjects[this.currentSubjectId].spentSeconds += timeDifferenceInSeconds;
this.topicChangeDate = currentTime; this.topicChangeDate = currentTime;
} }
round(val:number){
round(val: number) {
if (val < 60) { if (val < 60) {
return Math.round(val) +' s' return Math.round(val) + ' s'
} }
return (Math.round(val / 60) * 60) + ' min' return (Math.round(val / 60) * 60) + ' min'
} }
@ -73,12 +81,13 @@ export class AppComponent implements OnInit, OnDestroy {
if (boom[0]) { if (boom[0]) {
accumulatedDuration += this.findMinutesDurationInDescription(topic) | 0 accumulatedDuration += this.findMinutesDurationInDescription(topic) | 0
newTopics.push({ newTopics.push({
id: ii, id: ii,
title: boom[0], title: boom[0],
duration: 15, duration: 15,
spentSeconds: 0, spentSeconds: 0,
author: '', author: this.findAuthorInDescription(topic),
notes: '', notes: '',
finished: false, finished: false,
startDate: this.getStartDateAfterDuration(accumulatedDuration + ''), startDate: this.getStartDateAfterDuration(accumulatedDuration + ''),
@ -214,30 +223,30 @@ export class AppComponent implements OnInit, OnDestroy {
} }
makeStatisticsOnTopicsSpentSeconds() { makeStatisticsOnTopicsSpentSeconds() {
let totalSeconds = 0; let totalSeconds = 0;
for (const topic of this.subjects) { for (const topic of this.subjects) {
totalSeconds += topic.spentSeconds; totalSeconds += topic.spentSeconds;
}
let averageSeconds = totalSeconds / this.subjects.length;
let longestTopic = this.subjects[0];
let longestSeconds = longestTopic.spentSeconds;
for (const topic of this.subjects) {
if (topic.spentSeconds > longestSeconds) {
longestSeconds = topic.spentSeconds;
longestTopic = topic;
} }
}
let shortestTopic = this.subjects[0]; let averageSeconds = totalSeconds / this.subjects.length;
let shortestSeconds = shortestTopic.spentSeconds;
for (const topic of this.subjects) { let longestTopic = this.subjects[0];
if (topic.spentSeconds < shortestSeconds) { let longestSeconds = longestTopic.spentSeconds;
shortestSeconds = topic.spentSeconds; for (const topic of this.subjects) {
shortestTopic = topic; if (topic.spentSeconds > longestSeconds) {
longestSeconds = topic.spentSeconds;
longestTopic = topic;
}
}
let shortestTopic = this.subjects[0];
let shortestSeconds = shortestTopic.spentSeconds;
for (const topic of this.subjects) {
if (topic.spentSeconds < shortestSeconds) {
shortestSeconds = topic.spentSeconds;
shortestTopic = topic;
}
} }
}
this.statsExplication = `Total spent seconds: ${totalSeconds}\nAverage spent seconds: ${averageSeconds}\nLongest topic: ${longestTopic.title} (${longestSeconds} seconds)\nShortest topic: ${shortestTopic.title} (${shortestSeconds} seconds)`; this.statsExplication = `Total spent seconds: ${totalSeconds}\nAverage spent seconds: ${averageSeconds}\nLongest topic: ${longestTopic.title} (${longestSeconds} seconds)\nShortest topic: ${shortestTopic.title} (${shortestSeconds} seconds)`;
@ -249,21 +258,87 @@ export class AppComponent implements OnInit, OnDestroy {
this.updateTopicChangeDate() this.updateTopicChangeDate()
} }
compteRendu:string = '' compteRendu: string = ''
formatDateYMD(date: Date) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return `${year}-${month}-${day}`;
}
buildCompteRendu(){ buildCompteRendu() {
let compteRendu = 'Compte rendu :\n\n'; let compteRendu = `Compte rendu du ${this.formatDateYMD(this.today)}\n`;
compteRendu += ` début: ${this.startTime}, fin:${this.endTime}.\n`;
compteRendu += ` Présents: ${this.formatPresentLines()}.\n`;
if (this.scribe) {
compteRendu += ` Scribe: ${this.scribe}.\n`;
}
if (this.timekeeper) {
compteRendu += ` Gardien du temps: ${this.timekeeper}.\n`;
}
if (this.animator) {
compteRendu += ` Animation: ${this.animator}.\n`;
}
for (const topic of this.subjects) { for (const topic of this.subjects) {
compteRendu += `Sujet ${topic.id + 1} : ${topic.title}\n`; compteRendu += `* ${topic.title}-`;
compteRendu += `Durée : ${topic.duration} minutes\n`; compteRendu += ` ${topic.duration} min`;
compteRendu += `Temps écoulé : ${this.resteTopicMinutes(topic)}\n\n`; compteRendu += ` (${topic.author})\n`;
compteRendu += ` \n${topic.notes}\n`;
compteRendu += `Temps écoulé : ${this.round(topic.spentSeconds)}\n\n`;
} }
this.compteRendu = compteRendu this.compteRendu = compteRendu
return compteRendu return compteRendu
} }
copyCompteRenduToClipboard() {
/**
* returns each present person for each line with a dash at the beggining if missing
*/
formatPresentLines(){
let lines = ''
if (this.presents) {
lines += this.presents.split('\n').map(line => {
if(!line.length){
return '';
}
if (!line.startsWith('- ')) {
return `- ${line}`;
}
return line;
}).join('\n') + '\n'
}
return lines;
}
copyCompteRenduToClipboard() {
navigator.clipboard.writeText(this.compteRendu); navigator.clipboard.writeText(this.compteRendu);
} }
private findAuthorInDescription(topic: string): string {
let authorRegex = /\(([^)]+)\)/g;
let matches = authorRegex.exec(topic);
if (matches) {
return matches[1];
}
return '';
}
countLinesInPresent() {
return this.presents.split('\n').length;
}
downloadCompteRendu() {
let fichier = new Blob([this.buildCompteRendu()], {type: 'text/plain'});
let lien = document.createElement('a');
lien.setAttribute('href', window.URL.createObjectURL(fichier));
lien.setAttribute('download', 'compte-rendu.txt');
lien.click();
}
getMinutesBetweenTwoDates(date1: Date, date2: Date) {
return Math.floor((date2.getTime() - date1.getTime()) / 60000);
}
} }