示例#1
0
        public async Task SiteInfoTest()
        {
            var site = await WikidataSiteAsync;
            var info = WikibaseSiteInfo.FromSiteInfo(site.SiteInfo);

            Assert.Equal("http://www.wikidata.org/entity/", info.ConceptBaseUri);
            Assert.Equal("https://commons.wikimedia.org/wiki/", info.GeoShapeStorageBaseUri);
            Assert.Equal("https://commons.wikimedia.org/wiki/", info.TabularDataStorageBaseUri);
            Assert.Equal("http://www.wikidata.org/entity/Q50", info.MakeEntityUri("Q50"));
            Assert.Equal("Q123", info.ParseEntityId("http://www.wikidata.org/entity/Q123"));
        }
示例#2
0
        /// <inheritdoc />
        protected override async Task ProcessRecordAsync(CancellationToken cancellationToken)
        {
            var options = EntityEditOptions.Bulk;

            if (Bot)
            {
                options |= EntityEditOptions.Bot;
            }
            var entity = new Entity(SourceSite, SourceId);
            await entity.RefreshAsync(EntityQueryOptions.FetchClaims
                                      | EntityQueryOptions.FetchInfo, null, cancellationToken);

            if (!entity.Exists)
            {
                throw new InvalidOperationException($"The source entity {entity} does not exist.");
            }
            var dest = new Entity(DestinationSite, DestinationId);
            await dest.RefreshAsync(EntityQueryOptions.FetchInfo | EntityQueryOptions.FetchClaims);

            if (!dest.Exists)
            {
                throw new InvalidOperationException($"The destination entity {dest} does not exist.");
            }

            sourceSiteWikibaseInfo      = WikibaseSiteInfo.FromSiteInfo(SourceSite.SiteInfo);
            destinationSiteWikibaseInfo = WikibaseSiteInfo.FromSiteInfo(DestinationSite.SiteInfo);

            var refProp = CitationProperty == null ? null : new Entity(DestinationSite, CitationProperty);
            // Lookup for the claims that are previously imported from wikidata
            // Claim ID --> Claim
            Dictionary <string, Claim> existingClaims = null;

            if (refProp != null)
            {
                await refProp.RefreshAsync(EntityQueryOptions.FetchInfo);

                existingClaims = dest.Claims.Select(c => new
                {
                    Id = c.References.Select(r =>
                                             (string)r.Snaks.FirstOrDefault(s => s.PropertyId == refProp.Id)?.DataValue)
                         .FirstOrDefault(s => s != null),
                    Claim = c
                }).Where(t => t.Id != null)
                                 .ToDictionary(t => t.Id, t => t.Claim);
            }
            var newClaims    = new List <Claim>();
            var optionalProp = OptionalProperty == null ? null : new HashSet <string>(OptionalProperty);

            bool IsPropertyValueOptional(string propertyId)
            {
                return(optionalProp?.Contains(propertyId) ?? false);
            }

            IEnumerable <string> props = Property;
            var newClaimsCounter       = 0;
            var updatedClaimsCounter   = 0;

            if (Property.Length == 1 && Property[0] == "*")
            {
                if (EntityMapping == null)
                {
                    throw new ArgumentNullException(nameof(EntityMapping));
                }
                props = EntityMapping.Keys.Cast <string>();
            }
            foreach (var prop in props)
            {
                if (prop == null)
                {
                    throw new ArgumentException("Properties have null item.", nameof(Property));
                }
                var pc = entity.Claims[prop.Trim().ToUpperInvariant()];
                foreach (var claim in pc)
                {
                    if (existingClaims != null && existingClaims.TryGetValue(claim.Id, out var newClaim))
                    {
                        var updated = false;
                        if (!SnakValueEquals(claim.MainSnak, newClaim.MainSnak, IsPropertyValueOptional(claim.MainSnak.PropertyId)))
                        {
                            newClaim.MainSnak.SnakType     = claim.MainSnak.SnakType;
                            newClaim.MainSnak.RawDataValue = claim.MainSnak.RawDataValue;
                            FixValueEntityReference(newClaim.MainSnak, IsPropertyValueOptional(claim.MainSnak.PropertyId));
                            updated = true;
                        }
                        var qualifiers = new List <Snak>();
                        var reusedQs   = 0;
                        var newQs      = 0;
                        foreach (var q in claim.Qualifiers)
                        {
                            var mapped = MapEntity(q.PropertyId, true);
                            if (mapped == null)
                            {
                                continue;
                            }
                            var newQ = newClaim.Qualifiers.FirstOrDefault(ourQ =>
                                                                          ourQ.PropertyId == mapped && SnakValueEquals(q, ourQ, IsPropertyValueOptional(q.PropertyId)));
                            if (newQ == null)
                            {
                                newQs++;
                                newQ = new Snak(mapped)
                                {
                                    DataType = q.DataType, RawDataValue = q.RawDataValue
                                };
                                FixValueEntityReference(newQ, optionalProp?.Contains(q.PropertyId) ?? false);
                            }
                            else
                            {
                                reusedQs++;
                            }
                            qualifiers.Add(newQ);
                        }
                        if (newQs > 0 || reusedQs < newClaim.Qualifiers.Count)
                        {
                            newClaim.Qualifiers.Clear();
                            newClaim.Qualifiers.AddRange(qualifiers);
                            updated = true;
                        }
                        if (!updated)
                        {
                            continue;
                        }
                        updatedClaimsCounter++;
                    }
                    else
                    {
                        newClaimsCounter++;
                        newClaim = new Claim(CloneSnak(claim.MainSnak, IsPropertyValueOptional(claim.MainSnak.PropertyId)));
                        foreach (var qualifier in claim.Qualifiers)
                        {
                            var snak = CloneSnak(qualifier, IsPropertyValueOptional(qualifier.PropertyId));
                            if (snak == null)
                            {
                                continue;
                            }
                            newClaim.Qualifiers.Add(snak);
                        }
                    }
                    if (refProp != null)
                    {
                        if (newClaim.References.All(r => (string)r.Snaks.FirstOrDefault(s => s.PropertyId == refProp.Id)?.DataValue != claim.Id))
                        {
                            var refSnak = new Snak(refProp.Id, claim.Id, refProp.DataType);
                            newClaim.References.Add(new ClaimReference(refSnak));
                        }
                    }
                    newClaims.Add(newClaim);
                }
            }
            var changes = new List <EntityEditEntry>();

            changes.AddRange(newClaims.Select(c => new EntityEditEntry(nameof(dest.Claims), c)));
            if (changes.Count == 0)
            {
                WriteCommandDetail($"No matching claims to copy from \"{entity.Id}\"({SourceSite}) to {dest.Id}({DestinationSite})");
                return;
            }
            if (!ShouldProcess(string.Format("{0}({1}) --> {2}({3}), {4} changes. Claims: +{5}, !{6}",
                                             entity.Id, entity.Site, dest.Id, dest.Site, changes.Count, newClaimsCounter, updatedClaimsCounter)))
            {
                return;
            }
            if (Progressive)
            {
                int counter = 1;
                foreach (var change in changes)
                {
                    var claim = (Claim)change.Value;
                    if (ShouldProcess(string.Format("{0}({1}) --> {2}({3}), Target claim: {4}",
                                                    entity.Id, entity.Site, dest.Id, dest.Site, claim)))
                    {
                        await dest.EditAsync(new[] { change },
                                             $"[{counter}/{changes.Count}] Adding {newClaimsCounter} / updating {updatedClaimsCounter} claims from \"{entity.Id}\" on {SourceSite}.",
                                             options, cancellationToken);
                    }
                }
            }
            else
            {
                await dest.EditAsync(changes,
                                     $"Added {newClaimsCounter} / updated {updatedClaimsCounter} claims from \"{entity.Id}\" on {SourceSite}.",
                                     options, cancellationToken);
            }
            WriteCommandDetail($"Added {newClaimsCounter} / updated {updatedClaimsCounter} claims from \"{entity.Id}\"({SourceSite}) to {dest.Id}({DestinationSite})");
        }