Conversie evenimente calendaristice

Se dau 2 API-uri cu evenimente calendaristice pe care trebuie sa le copii/introduc din API_1 in API_2. API_1 are the conceptul de recurenta iar celalalt nu.

Iata un set de date provenind din API_1

Starts,Ends,Schedule
9/23/2019 12:00:00 PM,9/23/2019 1:00:00 PM,"Weekly on Monday, until June 29, 2020"
1/8/2020 12:00:00 PM,1/8/2020 1:00:00 PM,Once
10/17/2019 12:00:00 PM,10/17/2019 1:00:00 PM,"Weekly on Thursday, until April 2"
10/18/2019 12:00:00 PM,10/18/2019 1:00:00 PM,"Weekly on Friday, until April 10"
1/31/2020 12:00:00 PM,1/31/2020 1:00:00 PM,Once
1/17/2020 12:00:00 PM,1/17/2020 1:00:00 PM,Once
1/24/2020 12:00:00 PM,1/24/2020 1:00:00 PM,Once
10/3/2019 12:00:00 PM,10/3/2019 1:00:00 PM,"Weekly on Thursday, until April 30, 2020"
1/24/2019 9:30:00 AM,1/24/2019 11:30:00 AM,"Weekly on Thursday, for a year"
9/17/2019 1:00:00 PM,9/17/2019 3:00:00 PM,"Weekly on Tuesday, until May 26, 2020"
1/14/2020 4:00:00 PM,1/14/2020 5:00:00 PM,Once
10/22/2019 11:30:00 AM,10/22/2019 1:00:00 PM,"Weekly on Tuesday, until May 19, 2020"
2/20/2019 6:00:00 PM,2/20/2019 9:00:00 PM,"Weekly on Wednesday, for a year"
1/20/2020 12:00:00 AM,1/21/2020 12:00:00 AM,Once
9/3/2019 12:00:00 PM,9/3/2019 1:00:00 PM,"Weekly on Tuesday, until May 19, 2020"
9/9/2019 12:00:00 PM,9/9/2019 1:00:00 PM,"Weekly on Monday, until May 18, 2020"
1/1/2020 12:00:00 AM,1/2/2020 12:00:00 AM,Once
9/18/2019 12:00:00 PM,9/18/2019 1:00:00 PM,"Weekly on Wednesday, until May 27, 2020"
1/29/2020 4:00:00 PM,1/29/2020 5:00:00 PM,Once
1/24/2020 1:15:00 PM,1/24/2020 2:15:00 PM,Once
1/10/2020 1:15:00 PM,1/10/2020 2:15:00 PM,Once
1/8/2019 5:30:00 PM,1/8/2019 6:30:00 PM,"Weekly on Tuesday, until January 19, 2021"
1/17/2019 1:00:00 PM,1/17/2019 2:30:00 PM,"Monthly on the third Thursday, until January 31, 2020"

Pentru evenimentele cu Schedule==“Once” e simplu. Pt restul ma gindesc sa parsez cimpul “Schedule” si sa calculez zilele din saptamina.

Pentru primul “Weekly” Starts ar deveni:

	1/6/2020 12:00:00 PM
	1/13/2020 12:00:00 PM
	1/20/2020 12:00:00 PM
	1/27/2020 12:00:00 PM
	2/3/2020 12:00:00 PM

Voi cum ati face?

Multzam

Eu as filtra acele evenimente dupa Weekly si Monthly. Dar in fisierul tau Starts are aceeasi data ca si Ends. Ceea ce este derutant.

Pe langa astea, nu cred ca este cea mai buna idee sa te bazezi pe ceea ce este in coloana de Schedule. Daca acel text se schimba si tu scrii codul bazand-te pe ceea ce este acum, nu mai merge nimic. Parerea mea.

@Cornel, de regula lucrurile trebuie incepute bine (banuiesc ca e vorba de continuarea acestui topic Mobile web app (Proof of concept)). Acolo vei avea probleme mari de parsare intrucat interfata ta de schimb de date cu alte aplicatii nu respecta un patern si ai situatii de genul

Weekly on Wednesday, for a year
Weekly on Wednesday, until May 27, 2020
Monthly on the third Thursday, until January 31, 2020

Foarte probabil ca sa iti mai apara si alte combinatii (eg Monthly on the second) ceea ce face parsarea dificila si predispusa la erori. Formatul acela e mai degraba human readable decat parsable. Iti sugerez sa umbli la sursa daca acest lucru e posibil.

[LE] Observ data intr-un format timezone. Nu face asta in data exchange, mai ales ca e vorba de o aplicatie de planificare a ceva. Foloseste UTC.

In primul rand serializeaza datele primite, apoi foloseste o librarie gen Carbon pt php sau MomentJs pt JS, librarii care iti permit transformarea din human readeable in formatul dorit de tine

Not much I can do about API_1… e riscul meseriei… Iar Starts nu e la fel cu Ends, pt ca e alta ora.

Pai asta este derutant!
Ends ala este cand se termina evenimentul sau se termina cand zice aici “Weekly on Monday, until June 29, 2020”?

Nu pot controla ce vine de la API_1, iar de parsat nu e chiar asa de rau:

my $now = DateTime->now;
my $next_month = $now->clone->add(DateTime::Duration->new(months => 1));
my $ordinals = {
        first => 1,
        second => 2,
        third => 3,
        fourth => 4,
        last => -1,
};

my $durations = {
        a => 1,
        one => 1,
        two => 2,
};

my $parser = DateTime::Format::Strptime->new(
        pattern => '%m/%d/%Y %I:%M:%S %P',
        time_zone => 'America/New_York',
        on_error => sub {
                print STDERR "ERR: @_\n";
        },
);

my $parser2 = DateTime::Format::Strptime->new(
        pattern => '%B %d, %Y',
        time_zone => 'America/New_York',


my $dt = $parser->parse_datetime($row->{Starts});
my ($freq, $wkday, $prep, $until) = $row->{Schedule} =~ /(Weekly|Monthly) on (.*day), (until|for) (.*)/;
my $dt_until;
if ($prep eq 'until') {
    unless ($until =~ /, 20\d\d/) {
        # add current year or next year, depending which month is specified
        # more work needed here..
        $until = $until . ", 2020";
    }
    $dt_until = $parser2->parse_datetime($until);
}
elsif ($prep eq 'for') {
    my ($count, $period) = $until =~ /(\w+) (\w+)/;
    print "o $count\t$durations->{$count}\t$period\n";
    if ($period =~ /year/) {
        $period = 'years';
    }
    elsif ($period =~ /month/) {
        $period = 'months';                         
    }
    $dt_until = $dt->clone->add(DateTime::Duration->new($period => $durations->{$count}));
}

my $byday = lc substr $wkday, 0, 2;
if ($freq eq 'Monthly' ) {
        my $mo_day;
        ($mo_day, $wkday) = $wkday =~ /the (\w+) (\w\w).*day/;
        if (exists $ordinals->{$mo_day}) {
                $byday = $ordinals->{$mo_day} . lc(substr($wkday, 0, 2));
        }
}

my $set = DateTime::Event::ICal->recur(
        dtstart => $dt,
        until => $dt_until,
        freq => lc $freq,
        byday => [ $byday ],
);

for my $rcdt ($set->as_list( start => $now, end => $next_month )) {
        print "\t", $rcdt, "\n";
};

si treaba asta produce ceva de genul asta:

[Weekly]	Monday	[until]	June 29, 2020
	2020-01-06T12:00:00
	2020-01-13T12:00:00
	2020-01-20T12:00:00
	2020-01-27T12:00:00
	2020-02-03T12:00:00
[Weekly]	Thursday	[until]	April 2
	2020-01-09T12:00:00
	2020-01-16T12:00:00
	2020-01-23T12:00:00
	2020-01-30T12:00:00
[Weekly]	Friday	[until]	April 10
	2020-01-10T12:00:00
	2020-01-17T12:00:00
	2020-01-24T12:00:00
	2020-01-31T12:00:00
[Weekly]	Thursday	[until]	April 30, 2020
	2020-01-09T12:00:00
	2020-01-16T12:00:00
	2020-01-23T12:00:00
	2020-01-30T12:00:00
[Weekly]	Thursday	[for]	a year
	2020-01-09T09:30:00
	2020-01-16T09:30:00
	2020-01-23T09:30:00
[Weekly]	Tuesday	[until]	May 26, 2020
	2020-01-07T13:00:00
	2020-01-14T13:00:00
	2020-01-21T13:00:00
	2020-01-28T13:00:00
	2020-02-04T13:00:00
[Weekly]	Tuesday	[until]	May 19, 2020
	2020-01-07T11:30:00
	2020-01-14T11:30:00
	2020-01-21T11:30:00
	2020-01-28T11:30:00
	2020-02-04T11:30:00
	2020-01-07T12:00:00
	2020-01-14T12:00:00
	2020-01-21T12:00:00
	2020-01-28T12:00:00
	2020-02-04T12:00:00
[Weekly]	Monday	[until]	May 18, 2020
	2020-01-06T12:00:00
	2020-01-13T12:00:00
	2020-01-20T12:00:00
	2020-01-27T12:00:00
	2020-02-03T12:00:00
[Weekly]	Wednesday	[until]	May 27, 2020
	2020-01-08T12:00:00
	2020-01-15T12:00:00
	2020-01-22T12:00:00
	2020-01-29T12:00:00
	2020-02-05T12:00:00
[Weekly]	Tuesday	[until]	January 19, 2021
	2020-01-07T17:30:00
	2020-01-14T17:30:00
	2020-01-21T17:30:00
	2020-01-28T17:30:00
	2020-02-04T17:30:00
[Monthly]	the third Thursday	[until]	January 31, 2020
	2020-01-16T13:00:00
[Monthly]	the last Monday	[until]	January 31, 2020
	2020-01-27T13:00:00

LE: Practic construiesc evenimente conform reculilor de recurenta pentru urmatoarele 30 de zile.
BTW, exemplul asta e in Perl. Ma gindeam sa incerc si dateutil.rrule din Python dar mai vedem.

Starts si Ends specifica prima instanta, si Schedule specifica durata si frecventa

Cea mai simplă soluție pare să fie să parsezi fiecare cuvânt din stringul “schedule”. Nu-i mare filozofie. Oricum ceva de genul ăsta cred că e implementat și în API1.

La o prima vedere a iesti asta. Nu este pe departe cea mai buna metoda. Incepe de astazi.

const neatCsv = require('neat-csv');
const fs = require('fs');

const file = "C:\\Users\\cosmi\\Desktop\\events.csv";

fs.readFile(file, async (err, data) => {
    if (err) {
        console.error(err);
        return
    }
    let content = await neatCsv(data);

    const weekly = content.filter(f => f.Schedule.includes("Weekly"));
    const monthly = content.filter(f => f.Schedule.includes("Monthly"));

    const recurrence = {
        "weekly" : 7,
        "monthly30" : 30,
        "monthly31" : 31
    };

    const weaklyBasisEvents = weekly.map((() => getDates(new Date(), new Date("June 29, 2020 13:00:00"), recurrence.weekly)));
    const monthlyBasisEvents = monthly.map((() => getDates(new Date(), new Date("June 29, 2020 13:00:00"), recurrence.monthly30)));
    console.log(weaklyBasisEvents);
    console.log("------------------");
    console.log(monthlyBasisEvents);


});


function getDates(start, end, recurrence) {

    let date = start;
    let dates = [];

    while((date = addDays(date, recurrence)) < end) {
        dates.push(date);
    }
    return dates;
}

function addDays(date, days) {
    let newDate = new Date(date);
    newDate.setDate(date.getDate() + days);
    return newDate;
}
}

Am presupus ca acel fisier este un csv si am filtrat liniile cu weekly si monthly. Mai am de parsat acel camp de Schedule si corectii la cod + sa il aranjez :slight_smile:

Dar asta poate maine. Si maine este o zi :sun_with_face: